diff options
author | Dan McDonald <danmcd@joyent.com> | 2020-10-22 10:51:12 -0400 |
---|---|---|
committer | Dan McDonald <danmcd@joyent.com> | 2020-10-22 10:51:12 -0400 |
commit | ebcb78defa820eaf313b5b3936a1bfe49002f105 (patch) | |
tree | b63f0ad26d0a4440299f76bf80957eb1340168ef | |
parent | 54c8d80ce91aece72e185f6585e3ca8d62a0ea31 (diff) | |
parent | 1d276e0b382cf066dae93640746d8b4c54d15452 (diff) | |
download | illumos-joyent-ebcb78defa820eaf313b5b3936a1bfe49002f105.tar.gz |
[illumos-gate merge]
commit 1d276e0b382cf066dae93640746d8b4c54d15452
13242 parse_user_name in PAM is sloppy
commit 549e0fd315406a4a97f9043f44860eed39a715da
13213 Want development driver for accessing AMD DF
commit becd642c1e97d1674cef9e3dccb159c20c6992ae
13212 Add Renoir to amdzen(7D)
commit 047043c2181e939608ea2c09257fd2d515e99643
13144 refactor amdf17nbdf into a nexus
13145 rewrite amdf17nbdf to use the ksensor framework
13146 Want a driver for AMD SMN user access
39 files changed, 3238 insertions, 1101 deletions
@@ -622,6 +622,7 @@ f kernel/drv/aac.conf 0644 root sys f kernel/drv/acpi_drv.conf 0644 root sys f kernel/drv/adpu320.conf 0644 root sys f kernel/drv/aggr.conf 0644 root sys +f kernel/drv/amdzen.conf 0644 root sys d kernel/drv/amd64 0755 root sys f kernel/drv/amd64/aac 0755 root sys f kernel/drv/amd64/acpi_drv 0755 root sys @@ -630,7 +631,8 @@ f kernel/drv/amd64/afe 0755 root sys f kernel/drv/amd64/aggr 0755 root sys f kernel/drv/amd64/ahci 0755 root sys f kernel/drv/amd64/amd8111s 0755 root sys -f kernel/drv/amd64/amdf17nbdf 0755 root sys +f kernel/drv/amd64/amdzen 0755 root sys +f kernel/drv/amd64/amdzen_stub 0755 root sys f kernel/drv/amd64/amdnbtemp 0755 root sys f kernel/drv/amd64/amr 0755 root sys f kernel/drv/amd64/arcmsr 0755 root sys @@ -769,6 +771,7 @@ f kernel/drv/amd64/si3124 0755 root sys f kernel/drv/amd64/simnet 0755 root sys f kernel/drv/amd64/skd 0755 root sys f kernel/drv/amd64/smbios 0755 root sys +f kernel/drv/amd64/smntemp 0755 root sys f kernel/drv/amd64/smp 0755 root sys f kernel/drv/amd64/smrt 0755 root sys f kernel/drv/amd64/softmac 0755 root sys @@ -19558,8 +19561,9 @@ f usr/share/man/man7d/afe.7d 0444 root bin f usr/share/man/man7d/ahci.7d 0444 root bin s usr/share/man/man7d/allkmem.7d=mem.7d f usr/share/man/man7d/amd8111s.7d 0444 root bin -f usr/share/man/man7d/amdf17nbdf.7d 0444 root bin f usr/share/man/man7d/amdnbtemp.7d 0444 root bin +f usr/share/man/man7d/amdzen.7d 0444 root bin +s usr/share/man/man7d/amdzen_stub.7d=amdzen.7d f usr/share/man/man7d/amr.7d 0444 root bin f usr/share/man/man7d/arcmsr.7d 0444 root bin f usr/share/man/man7d/asy.7d 0444 root bin @@ -19665,6 +19669,7 @@ f usr/share/man/man7d/sgen.7d 0444 root bin f usr/share/man/man7d/si3124.7d 0444 root bin f usr/share/man/man7d/skd.7d 0444 root bin f usr/share/man/man7d/smbios.7d 0444 root bin +f usr/share/man/man7d/smntemp.7d 0444 root bin f usr/share/man/man7d/st.7d 0444 root bin f usr/share/man/man7d/sysmsg.7d 0444 root bin f usr/share/man/man7d/systrace.7d 0444 root bin diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 6d6276c70d..32329de0e9 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -488,6 +488,7 @@ i386_SUBDIRS= \ acpihpd \ addbadsec \ ahciem \ + amdzen \ bhyve \ bhyvectl \ biosdev \ diff --git a/usr/src/cmd/amdzen/Makefile b/usr/src/cmd/amdzen/Makefile new file mode 100644 index 0000000000..c9f8b7778e --- /dev/null +++ b/usr/src/cmd/amdzen/Makefile @@ -0,0 +1,32 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +PROG= usmn udf + +include ../Makefile.cmd + +ROOTCMDDIR = $(ROOTLIB) +CPPFLAGS += -I$(SRC)/uts/intel/io/amdzen +CFLAGS += $(CCVERBOSE) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTCMD) + +clean: + +include ../Makefile.targ diff --git a/usr/src/cmd/amdzen/udf.c b/usr/src/cmd/amdzen/udf.c new file mode 100644 index 0000000000..604e0b4802 --- /dev/null +++ b/usr/src/cmd/amdzen/udf.c @@ -0,0 +1,118 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +/* + * Facilitate access to the AMD Zen data fabric + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <zen_udf.h> + +static void +udf_readone(int fd, uint8_t inst, uint8_t func, uint16_t reg, boolean_t do64) +{ + int ret; + zen_udf_io_t zui; + + bzero(&zui, sizeof (zui)); + zui.zui_inst = inst; + zui.zui_func = func; + zui.zui_reg = reg; + + ret = ioctl(fd, do64 ? ZEN_UDF_READ64 : ZEN_UDF_READ32, &zui); + if (ret != 0) { + err(EXIT_FAILURE, "failed to issue read ioctl"); + } + + (void) printf("ifr %x/%x/%x: 0x%" PRIx64 "\n", + inst, func, reg, zui.zui_data); +} + +int +main(int argc, char *argv[]) +{ + int c, fd; + const char *device = NULL; + const char *funcstr = NULL; + const char *inststr = NULL; + const char *regstr = NULL; + uint8_t func, inst; + uint16_t reg; + unsigned long lval; + char *eptr; + boolean_t do64 = B_FALSE; + + while ((c = getopt(argc, argv, "d:f:i:r:l")) != -1) { + switch (c) { + case 'd': + device = optarg; + break; + case 'f': + funcstr = optarg; + break; + case 'i': + inststr = optarg; + break; + case 'l': + do64 = B_TRUE; + break; + case 'r': + regstr = optarg; + break; + } + } + + if (device == NULL || funcstr == NULL || inststr == NULL || + regstr == NULL) { + warnx("missing required arguments"); + (void) fprintf(stderr, "Usage: udf [-l] -d device -f func -i " + "inst -r reg\n"); + } + + errno = 0; + lval = strtoul(funcstr, &eptr, 0); + if (errno != 0 || lval > UINT8_MAX || *eptr != '\0') { + errx(EXIT_FAILURE, "failed to parse -f: %s", funcstr); + } + func = (uint8_t)lval; + + lval = strtoul(inststr, &eptr, 0); + if (errno != 0 || lval > UINT8_MAX || *eptr != '\0') { + errx(EXIT_FAILURE, "failed to parse -i: %s", inststr); + } + inst = (uint8_t)lval; + + lval = strtoul(regstr, &eptr, 0); + if (errno != 0 || lval > UINT16_MAX || *eptr != '\0') { + errx(EXIT_FAILURE, "failed to parse -r: %s", regstr); + } + reg = (uint16_t)lval; + + if ((fd = open(device, O_RDONLY)) < 0) { + err(EXIT_FAILURE, "failed to open %s", device); + } + + udf_readone(fd, inst, func, reg, do64); + (void) close(fd); + return (0); +} diff --git a/usr/src/cmd/amdzen/usmn.c b/usr/src/cmd/amdzen/usmn.c new file mode 100644 index 0000000000..3b9998f021 --- /dev/null +++ b/usr/src/cmd/amdzen/usmn.c @@ -0,0 +1,100 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +/* + * Read and write to the AMD SMN. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <usmn.h> + +static boolean_t +usmn_read(int fd, const char *addr) +{ + unsigned long long l; + char *eptr; + usmn_reg_t usr; + + errno = 0; + l = strtoull(addr, &eptr, 16); + if (errno != 0 || *eptr != '\0' || l > UINT32_MAX) { + warnx("failed to parse %s: invalid string or address", addr); + return (B_FALSE); + } + + usr.usr_addr = (uint32_t)l; + usr.usr_data = 0; + + if (ioctl(fd, USMN_READ, &usr) != 0) { + warn("failed to read SMN at 0x%x", usr.usr_addr); + return (B_FALSE); + } + + (void) printf("0x%x: 0x%x\n", usr.usr_addr, usr.usr_data); + return (B_TRUE); +} + +int +main(int argc, char *argv[]) +{ + int i, c, fd, ret; + const char *device = NULL; + + while ((c = getopt(argc, argv, "d:")) != -1) { + switch (c) { + case 'd': + device = optarg; + break; + default: + (void) fprintf(stderr, "Usage: usmn -d device addr " + "[addr]...\n" + "Note: All addresses are interpreted as hex\n"); + return (2); + } + } + + if (device == NULL) { + errx(EXIT_FAILURE, "missing required device"); + } + + argc -= optind; + argv += optind; + + if (argc == 0) { + errx(EXIT_FAILURE, "missing registers to read"); + } + + if ((fd = open(device, O_RDONLY)) < 0) { + err(EXIT_FAILURE, "failed to open %s", device); + } + + ret = EXIT_SUCCESS; + for (i = 0; i < argc; i++) { + if (!usmn_read(fd, argv[i])) { + ret = EXIT_FAILURE; + } + } + + (void) close(fd); + + return (ret); +} diff --git a/usr/src/lib/libpam/pam_framework.c b/usr/src/lib/libpam/pam_framework.c index b3340ffd5e..9c46218812 100644 --- a/usr/src/lib/libpam/pam_framework.c +++ b/usr/src/lib/libpam/pam_framework.c @@ -24,7 +24,7 @@ */ /* - * Copyright (c) 2019, Joyent, Inc. + * Copyright 2020, Joyent, Inc. */ #include <syslog.h> @@ -656,9 +656,10 @@ parse_user_name(char *user_input, char **ret_username) * - we skip leading whitespaces and ignore trailing whitespaces */ while (*ptr != '\0') { - if ((*ptr == ' ') || (*ptr == '\t')) + if ((*ptr == ' ') || (*ptr == '\t') || + (index >= PAM_MAX_RESP_SIZE)) { break; - else { + } else { username[index] = *ptr; index++; ptr++; @@ -666,9 +667,9 @@ parse_user_name(char *user_input, char **ret_username) } /* ret_username will be freed in pam_get_user(). */ - if ((*ret_username = malloc(index + 1)) == NULL) + if (index >= PAM_MAX_RESP_SIZE || + (*ret_username = strdup(username)) == NULL) return (PAM_BUF_ERR); - (void) strcpy(*ret_username, username); return (PAM_SUCCESS); } diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile index bd7a7006c6..b762d59742 100644 --- a/usr/src/man/man7d/Makefile +++ b/usr/src/man/man7d/Makefile @@ -182,8 +182,8 @@ sparc_MANFILES= audiocs.7d \ i386_MANFILES= ahci.7d \ amd8111s.7d \ - amdf17nbdf.7d \ amdnbtemp.7d \ + amdzen.7d \ amr.7d \ arcmsr.7d \ arn.7d \ @@ -237,14 +237,17 @@ i386_MANFILES= ahci.7d \ si3124.7d \ skd.7d \ smbios.7d \ + smntemp.7d \ uath.7d \ ural.7d \ urtw.7d \ + usmn.7d \ vioblk.7d \ vioif.7d \ virtio.7d \ wpi.7d \ xhci.7d \ + zen_udf.7d \ zyd.7d _MANLINKS= 1394.7d \ @@ -260,9 +263,13 @@ _MANLINKS= 1394.7d \ sparc_MANLINKS= drmach.7d +i386_MANLINKS= amdzen_stub.7d + MANFILES = $(_MANFILES) $($(MACH)_MANFILES) MANLINKS = $(_MANLINKS) $($(MACH)_MANLINKS) +amdzen_stub.7d := LINKSRC = amdzen.7d + drmach.7d := LINKSRC = dr.7d fdc.7d := LINKSRC = fd.7d diff --git a/usr/src/man/man7d/amdnbtemp.7d b/usr/src/man/man7d/amdnbtemp.7d index 12e1ab5329..a086ebce99 100644 --- a/usr/src/man/man7d/amdnbtemp.7d +++ b/usr/src/man/man7d/amdnbtemp.7d @@ -61,11 +61,11 @@ are not supported at this time. For AMD Family 17h .Pq Zen processors, the -.Xr amdf17nbdf 7D +.Xr smntemp 7D driver provides access to the temperature sensors. .Sh SEE ALSO .Xr fmadm 1M , -.Xr amdf17nbdf 7D +.Xr smntemp 7D .Rs .%A AMD .%B BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 16h Models 00h-0Fh Processors diff --git a/usr/src/man/man7d/amdzen.7d b/usr/src/man/man7d/amdzen.7d new file mode 100644 index 0000000000..171d36b0d6 --- /dev/null +++ b/usr/src/man/man7d/amdzen.7d @@ -0,0 +1,50 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" +.\" Copyright 2020 Oxide Computer Company +.\" +.Dd September 1, 2020 +.Dt AMDZEN 7D +.Os +.Sh NAME +.Nm amdzen , +.Nm amdzen_stub +.Nd AMD Zen Nexus Driver +.Sh DESCRIPTION +The +.Sy amdzen +driver provides access to the AMD Zen, Zen+, and Zen 2 +.Pq Family 17h +processor families Northbridge, Data Fabric, and System Management Network +.Pq SMN . +This driver is a nexus driver and facilitates access to these devices +between various other devices such as +.Xr smntemp 7D . +The various processor devices that make up the northbridge and data +fabric have the +.Sy amdzen_stub +driver attached to them. +The different devices are all amalgamated and a single uniform view is +provided by the +.Sy amdzen +driver. +.Sh ARCHITECTURE +The +.Sy amdzen +and +.Sy amdzen_stub +drivers are limited to +.Sy x86 +platforms with AMD Family 17h processors. +.Sh SEE ALSO +.Xr smntemp 7D , +.Xr usmn 7D , +.Xr zen_udf 7D diff --git a/usr/src/man/man7d/amdf17nbdf.7d b/usr/src/man/man7d/smntemp.7d index e6e6ce863d..903d060825 100644 --- a/usr/src/man/man7d/amdf17nbdf.7d +++ b/usr/src/man/man7d/smntemp.7d @@ -10,24 +10,25 @@ .\" .\" .\" Copyright 2019, Joyent, Inc. +.\" Copyright 2020 Oxide Computer Company .\" -.Dd November 16, 2019 -.Dt AMDF17NBDF 7D +.Dd September 1, 2020 +.Dt SMNTEMP 7D .Os .Sh NAME -.Nm amdf17nbdf -.Nd AMD Family 17h Northbridge and Data Fabric Driver +.Nm smntemp +.Nd AMD SMN Temperature Sensor Driver .Sh SYNOPSIS .Pa /dev/sensors/temperature/cpu/* .Sh DESCRIPTION The .Nm -driver provides the system access to the Northbridge and Data Fabric -devices on AMD Family 17h +driver provides the system access to the temperature sensor found in the +AMD System Management Network +.Pq SMN +on AMD Family 17h .Pq Zen, Zen+, and Zen 2 -processors allowing the operating system to communicate with the system -management unit -.Pq SMU . +processors. .Pp From this, the driver exposes temperature sensors. On Family 17h systems, temperature sensors exist for each Zeppelin die, @@ -45,7 +46,9 @@ subject to change without notice, and should not be used directly. Raw temperature information can be dumped through the FMA developer utility fmtopo. .Sh SEE ALSO -.Xr fmadm 1M +.Xr fmadm 1M , +.Xr amdnbtemp 7D , +.Xr amdzen 7D .Rs .%A AMD .%B Open-Source Register Reference For AMD Family 17h Processors Models 00h-2Fh diff --git a/usr/src/man/man7d/usmn.7d b/usr/src/man/man7d/usmn.7d new file mode 100644 index 0000000000..b36da78be4 --- /dev/null +++ b/usr/src/man/man7d/usmn.7d @@ -0,0 +1,37 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" +.\" Copyright 2020 Oxide Computer Company +.\" +.Dd September 1, 2020 +.Dt USMN 7D +.Os +.Sh NAME +.Nm usmn +.Nd AMD SMN user access driver +.Sh SYNOPSIS +.Pa /devices/pseudo/amdzen@0/usmn@2:usmn.* +.Sh DESCRIPTION +The +.Nm +driver provides the ability to read data from the AMD system management +network +.Pq SMN +on AMD Family 17h +.Pq Zen, Zen+, and Zen 2 +processors. +.Pp +This driver is intended strictly for facilitating platform development +and is not recommended for systems that aren't doing kernel development +on AMD Zen platforms. +.Sh SEE ALSO +.Xr amdzen 7D , +.Xr zen_udf 7D diff --git a/usr/src/man/man7d/zen_udf.7d b/usr/src/man/man7d/zen_udf.7d new file mode 100644 index 0000000000..a2243ef67f --- /dev/null +++ b/usr/src/man/man7d/zen_udf.7d @@ -0,0 +1,36 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" +.\" Copyright 2020 Oxide Computer Company +.\" +.Dd October 7, 2020 +.Dt ZEN_UDF 7D +.Os +.Sh NAME +.Nm zen_udf +.Nd AMD data fabric user access driver +.Sh SYNOPSIS +.Pa /devices/pseudo/amdzen@0/zen_udf@3:zen_udf.* +.Sh DESCRIPTION +The +.Nm +driver provides the ability to read data from the AMD data fabric +.Pq DF +on AMD Family 17h +.Pq Zen, Zen+, and Zen 2 +processors. +.Pp +This driver is intended strictly for facilitating platform development +and is not recommended for systems that aren't doing kernel development +on AMD Zen platforms. +.Sh SEE ALSO +.Xr amdzen 7D , +.Xr usmn 7D diff --git a/usr/src/pkg/manifests/driver-cpu-amd-zen.mf b/usr/src/pkg/manifests/driver-cpu-amd-zen.mf new file mode 100644 index 0000000000..df4adf3339 --- /dev/null +++ b/usr/src/pkg/manifests/driver-cpu-amd-zen.mf @@ -0,0 +1,90 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/cpu/amd/zen@$(PKGVERS) +set name=pkg.description value="AMD Zen Nexus Driver" +set name=pkg.summary value="AMD Zen Nexus Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:System/Hardware +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +dir path=usr/share/man +dir path=usr/share/man/man7d +driver name=amdzen +# +# 1440-1447: f17h m70-7fh df +# 1448-144f: f17h m60-6fh df +# 1450: f17h m00-0f nb +# 1460-1467: f17h m00-0f df +# 1480: f17h m30-3f/70-7f nb +# 1490-1497: f17h m30-3f nb +# 15d0: f17h m10-m2f nb +# 15e8-15ef: f17h m10-m2f df +# 1630: f17h m60-6f nb +# +driver name=amdzen_stub \ + alias=pci1022,1440,p \ + alias=pci1022,1441,p \ + alias=pci1022,1442,p \ + alias=pci1022,1443,p \ + alias=pci1022,1444,p \ + alias=pci1022,1445,p \ + alias=pci1022,1446,p \ + alias=pci1022,1447,p \ + alias=pci1022,1448,p \ + alias=pci1022,1449,p \ + alias=pci1022,144a,p \ + alias=pci1022,144b,p \ + alias=pci1022,144c,p \ + alias=pci1022,144d,p \ + alias=pci1022,144e,p \ + alias=pci1022,144f,p \ + alias=pci1022,1450,p \ + alias=pci1022,1460,p \ + alias=pci1022,1461,p \ + alias=pci1022,1462,p \ + alias=pci1022,1463,p \ + alias=pci1022,1464,p \ + alias=pci1022,1465,p \ + alias=pci1022,1466,p \ + alias=pci1022,1467,p \ + alias=pci1022,1480,p \ + alias=pci1022,1490,p \ + alias=pci1022,1491,p \ + alias=pci1022,1492,p \ + alias=pci1022,1493,p \ + alias=pci1022,1494,p \ + alias=pci1022,1495,p \ + alias=pci1022,1496,p \ + alias=pci1022,1497,p \ + alias=pci1022,15d0,p \ + alias=pci1022,15e8,p \ + alias=pci1022,15e9,p \ + alias=pci1022,15ea,p \ + alias=pci1022,15eb,p \ + alias=pci1022,15ec,p \ + alias=pci1022,15ed,p \ + alias=pci1022,15ee,p \ + alias=pci1022,15ef,p \ + alias=pci1022,1630,p +file path=kernel/drv/$(ARCH64)/amdzen group=sys +file path=kernel/drv/$(ARCH64)/amdzen_stub group=sys +file path=kernel/drv/amdzen.conf group=sys +file path=usr/share/man/man7d/amdzen.7d +license lic_CDDL license=lic_CDDL +link path=usr/share/man/man7d/amdzen_stub.7d target=amdzen.7d diff --git a/usr/src/pkg/manifests/driver-cpu-sensor.mf b/usr/src/pkg/manifests/driver-cpu-sensor.mf index cf413d078c..7ac1e352ff 100644 --- a/usr/src/pkg/manifests/driver-cpu-sensor.mf +++ b/usr/src/pkg/manifests/driver-cpu-sensor.mf @@ -25,14 +25,6 @@ dir path=kernel/drv group=sys dir path=kernel/drv/$(ARCH64) group=sys dir path=usr/share/man dir path=usr/share/man/man7d -driver name=amdf17nbdf \ - alias=pci1022,1440,p \ - alias=pci1022,1450,p \ - alias=pci1022,1460,p \ - alias=pci1022,1480,p \ - alias=pci1022,1490,p \ - alias=pci1022,15d0,p \ - alias=pci1022,15e8,p driver name=amdnbtemp \ alias=pci1022,1203,p \ alias=pci1022,1303,p \ @@ -55,14 +47,16 @@ driver name=pchtemp \ alias=pci8086,a231,p \ alias=pci8086,a2b1,p \ alias=pci8086,a379,p -file path=kernel/drv/$(ARCH64)/amdf17nbdf group=sys +driver name=smntemp alias=smntemp file path=kernel/drv/$(ARCH64)/amdnbtemp group=sys file path=kernel/drv/$(ARCH64)/coretemp group=sys file path=kernel/drv/$(ARCH64)/pchtemp group=sys +file path=kernel/drv/$(ARCH64)/smntemp group=sys file path=kernel/drv/coretemp.conf group=sys -file path=usr/share/man/man7d/amdf17nbdf.7d file path=usr/share/man/man7d/amdnbtemp.7d file path=usr/share/man/man7d/coretemp.7d file path=usr/share/man/man7d/pchtemp.7d +file path=usr/share/man/man7d/smntemp.7d license lic_CDDL license=lic_CDDL +depend fmri=driver/cpu/amd/zen type=require depend fmri=system/ksensor type=require diff --git a/usr/src/pkg/manifests/driver-developer-amd-zen.mf b/usr/src/pkg/manifests/driver-developer-amd-zen.mf new file mode 100644 index 0000000000..7df997705f --- /dev/null +++ b/usr/src/pkg/manifests/driver-developer-amd-zen.mf @@ -0,0 +1,37 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/developer/amd/zen@$(PKGVERS) +set name=pkg.description value="AMD Zen Developer Drivers" +set name=pkg.summary value="Misc. AMD Zen Drivers for Platform Development" +set name=info.classification \ + value=org.opensolaris.category.2008:System/Hardware +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +dir path=usr/lib +dir path=usr/share/man +dir path=usr/share/man/man7d +driver name=usmn +driver name=zen_udf +file path=kernel/drv/$(ARCH64)/usmn group=sys +file path=kernel/drv/$(ARCH64)/zen_udf group=sys +file path=usr/lib/udf mode=0555 +file path=usr/lib/usmn mode=0555 +file path=usr/share/man/man7d/usmn.7d +file path=usr/share/man/man7d/zen_udf.7d +license lic_CDDL license=lic_CDDL diff --git a/usr/src/uts/common/io/usb/hcd/xhci/xhci.c b/usr/src/uts/common/io/usb/hcd/xhci/xhci.c index 2beeb69972..a28b1fa0d3 100644 --- a/usr/src/uts/common/io/usb/hcd/xhci/xhci.c +++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci.c @@ -1911,7 +1911,7 @@ xhci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, xhci_t *xhcip = ddi_get_soft_state(xhci_soft_state, getminor(dev) & ~HUBD_IS_ROOT_HUB); - if (secpolicy_xhci(credp) != 0 || + if (secpolicy_hwmanip(credp) != 0 || crgetzoneid(credp) != GLOBAL_ZONEID) return (EPERM); diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c index 861c748cff..00ca6ec54a 100644 --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -1381,7 +1381,7 @@ secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype) * this is required because vop_access function should lock the * node for reading. A three argument function should be defined * which accepts the following argument: - * A pointer to the internal "node" type (inode *) + * A pointer to the internal "node" type (inode *) * vnode access bits (VREAD|VWRITE|VEXEC) * a pointer to the credential * @@ -1453,8 +1453,8 @@ secpolicy_vnode_setattr(cred_t *cr, struct vnode *vp, struct vattr *vap, * * If you are the file owner: * chown to other uid FILE_CHOWN_SELF - * chown to gid (non-member) FILE_CHOWN_SELF - * chown to gid (member) <none> + * chown to gid (non-member) FILE_CHOWN_SELF + * chown to gid (member) <none> * * Instead of PRIV_FILE_CHOWN_SELF, FILE_CHOWN is also * acceptable but the first one is reported when debugging. @@ -2433,13 +2433,14 @@ secpolicy_gart_map(const cred_t *cr) } /* - * secpolicy_xhci + * secpolicy_hwmanip * - * Determine if the subject can observe and manipulate the xhci driver with a - * dangerous blunt hammer. Requires all privileges. + * Determine if the subject can observe and manipulate a hardware device with a + * dangerous blunt hammer, often suggests they can do something destructive. + * Requires all privileges. */ int -secpolicy_xhci(const cred_t *cr) +secpolicy_hwmanip(const cred_t *cr) { return (secpolicy_require_set(cr, PRIV_FULLSET, NULL, KLPDARG_NONE)); } diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h index 816d6995cf..9f1b80d390 100644 --- a/usr/src/uts/common/sys/policy.h +++ b/usr/src/uts/common/sys/policy.h @@ -53,12 +53,12 @@ typedef uint16_t in_port_t; * priv_policy_choice * determines extend of operation * audit on success - * returns a boolean_t indicating success (B_TRUE) or failure. + * returns a boolean_t indicating success (B_TRUE) or failure. * * priv_policy_only * when auditing is in appropriate (interrupt context) * to determine context of operation - * returns a boolean_t indicating success (B_TRUE) or failure. + * returns a boolean_t indicating success (B_TRUE) or failure. * */ int priv_policy(const cred_t *, int, boolean_t, int, const char *); @@ -166,7 +166,7 @@ int secpolicy_vnode_setid_retain(const cred_t *, boolean_t); int secpolicy_vnode_setids_setgids(const cred_t *, gid_t); int secpolicy_vnode_stky_modify(const cred_t *); int secpolicy_vscan(const cred_t *); -int secpolicy_xhci(const cred_t *); +int secpolicy_hwmanip(const cred_t *); int secpolicy_zinject(const cred_t *); int secpolicy_zfs(const cred_t *); int secpolicy_ucode_update(const cred_t *); diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files index baeccfaac8..1437fa19bd 100644 --- a/usr/src/uts/intel/Makefile.files +++ b/usr/src/uts/intel/Makefile.files @@ -434,3 +434,12 @@ PCHTEMP_OBJS = pchtemp.o # AMD Family 10h-16h temperature driver # AMDNBTEMP_OBJS = amdnbtemp.o + +# +# AMD Zen Nexus Driver +# +AMDZEN_OBJS = amdzen.o +AMDZEN_STUB_OBJS = amdzen_stub.o +SMNTEMP_OBJS = smntemp.o +USMN_OBJS = usmn.o +ZEN_UDF_OBJS = zen_udf.o diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel index 21210e9ef1..636eebcb94 100644 --- a/usr/src/uts/intel/Makefile.intel +++ b/usr/src/uts/intel/Makefile.intel @@ -784,7 +784,14 @@ LINTFLAGS += -D_MACHDEP -I$(UTSBASE)/i86pc # Sensor related drivers # DRV_KMODS += ksensor ksensor_test -DRV_KMODS += amdf17nbdf DRV_KMODS += coretemp DRV_KMODS += pchtemp DRV_KMODS += amdnbtemp +DRV_KMODS += smntemp + +# +# AMD Zen Nexus driver and Related +# +DRV_KMODS += amdzen +DRV_KMODS += amdzen_stub +DRV_KMODS += usmn zen_udf diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules index 763c448725..551896e151 100644 --- a/usr/src/uts/intel/Makefile.rules +++ b/usr/src/uts/intel/Makefile.rules @@ -149,6 +149,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amdnbtemp/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amdzen/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/amr/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) diff --git a/usr/src/uts/intel/amdzen/Makefile b/usr/src/uts/intel/amdzen/Makefile new file mode 100644 index 0000000000..1c7cf547cc --- /dev/null +++ b/usr/src/uts/intel/amdzen/Makefile @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +UTSBASE = ../.. + +MODULE = amdzen +OBJECTS = $(AMDZEN_OBJS:%=$(OBJS_DIR)/%) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/intel/io/amdzen + +include $(UTSBASE)/intel/Makefile.intel + +ALL_TARGET = $(BINARY) $(CONFMOD) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +install: $(INSTALL_DEPS) + +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/amdf17nbdf/Makefile b/usr/src/uts/intel/amdzen_stub/Makefile index a5543f176f..f14d44e526 100644 --- a/usr/src/uts/intel/amdf17nbdf/Makefile +++ b/usr/src/uts/intel/amdzen_stub/Makefile @@ -10,22 +10,22 @@ # # -# Copyright 2019, Joyent, Inc. +# Copyright 2020 Oxide Computer Company # UTSBASE = ../.. -MODULE = amdf17nbdf -OBJECTS = $(AMDF17NBDF_OBJS:%=$(OBJS_DIR)/%) +MODULE = amdzen_stub +OBJECTS = $(AMDZEN_STUB_OBJS:%=$(OBJS_DIR)/%) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/intel/io/amdf17nb include $(UTSBASE)/intel/Makefile.intel -ALL_TARGET = $(BINARY) $(CONFMOD) -LINT_TARGET = $(MODULE).lint +ALL_TARGET = $(BINARY) INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +LDFLAGS += -dy -Ndrv/amdzen + .KEEP_STATE: def: $(DEF_DEPS) @@ -36,12 +36,6 @@ clean: $(CLEAN_DEPS) clobber: $(CLOBBER_DEPS) -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - install: $(INSTALL_DEPS) include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c b/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c deleted file mode 100644 index 7be8a4a9f8..0000000000 --- a/usr/src/uts/intel/io/amdf17nbdf/amdf17nbdf.c +++ /dev/null @@ -1,1040 +0,0 @@ -/* - * This file and its contents are supplied under the terms of the - * Common Development and Distribution License ("CDDL"), version 1.0. - * You may only use this file in accordance with the terms of version - * 1.0 of the CDDL. - * - * A full copy of the text of the CDDL should have accompanied this - * source. A copy of the CDDL is also available via the Internet at - * http://www.illumos.org/license/CDDL. - */ - -/* - * Copyright 2019, Joyent, Inc. - */ - -/* - * AMD Family 17 Northbridge and Data Fabric Driver - * - * This driver attaches to the AMD Family 17h northbridge and data fabric bus. - * Each Zeppelin die ('processor node' in cpuid.c parlance) has its own - * northbridge and access to the data fabric bus. The northbridge and data - * fabric both provide access to various features such as: - * - * - The System Management Network (SMN) - * - Data Fabric via Fabric Indirect Config Access (FICAA) - * - * These are required to access things such as temperature sensors or memory - * controller configuration registers. - * - * In AMD Family 17h systems, the 'northbridge' is an ASIC that is part of the - * package that contains many I/O capabilities related to things like PCI - * express, etc. The 'data fabric' is the means by which different components - * both inside the socket and multiple sockets are connected together. Both the - * northbridge and the data fabric have dedicated PCI devices which the - * operating system can use to interact with them. - * - * ------------------------ - * Mapping Devices Together - * ------------------------ - * - * The operating system needs to expose things like temperature sensors and DRAM - * configuration registers in terms that are meaningful to the system such as - * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent - * the northbridge and data fabric; however, there are multiple PCI devices (one - * per die) that exist. This driver does manage to map all of these three things - * together; however, it requires some acrobatics. Unfortunately, there's no - * direct way to map a northbridge to its corresponding die. However, we can map - * a CPU die to a data fabric PCI device and a data fabric PCI device to a - * corresponding northbridge PCI device. - * - * In current Zen based products, there is a direct mapping between processor - * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and - * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to - * processor node 1, etc. This means that to map a logical CPU to a data fabric - * device, we take its processor node id, add it to 0x18 and find the PCI device - * that is on bus 0, device 0x18. As each data fabric device is attached based - * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the - * amd_f17nbdf_t structure. - * - * The northbridge PCI device has a defined device and function, but the PCI bus - * that it's on can vary. Each die has its own series of PCI buses that are - * assigned to it and the northbridge PCI device is on the first of die-specific - * PCI bus for each die. This also means that the northbridge will not show up - * on PCI bus 0, which is the PCI bus that all of the data fabric devices are - * on. While conventionally the northbridge with the lowest PCI bus value - * would correspond to processor node zero, hardware does not guarantee that at - * all. Because we don't want to be at the mercy of firmware, we don't rely on - * this ordering, even though we have yet to find a system that deviates from - * this scheme. - * - * One of the registers in the data fabric device's function 0 - * (AMDF17_DF_CFG_ADDR_CTL), happens to have the first PCI bus that is - * associated with the processor node. This means, that we can map a data fabric - * device to a northbridge by finding the northbridge whose PCI bus matches the - * value in the corresponding data fabric's AMDF17_DF_CFG_ADDR_CTL. - * - * This means that we can map a northbridge to a data fabric device and a data - * fabric device to a die. Because these are generally 1:1 mappings, there is a - * transitive relationship and therefore we know which northbridge is associated - * with which processor die. This is summarized in the following image: - * - * +-------+ +----------------------------+ +--------------+ - * | Die 0 | ---> | Data Fabric PCI BDF 0/18/0 |-------> | Northbridge | - * +-------+ | AMDF17_DF_CFG_ADDR: bus 10 | | PCI 10/0/0 | - * ... +----------------------------+ +--------------+ - * +-------+ +------------------------------+ +--------------+ - * | Die n | ---> | Data Fabric PCI BDF 0/18+n/0 |-------> | Northbridge | - * +-------+ | AMDF17_DF_CFG_ADDR: bus 133 | | PCI 133/0/0 | - * +------------------------------+ +--------------+ - * - * Note, the PCI buses used by the northbridges here are arbitrary. They do not - * reflect the actual values by hardware; however, the bus/device/function (BDF) - * of the data fabric accurately models hardware. All of the BDF values are in - * hex. - * - * Starting with the Rome generation of processors (Family 17h Model 30-3Fh), - * AMD has multiple northbridges that exist on a given die. All of these - * northbridges share the same data fabric and system management network port. - * From our perspective this means that some of the northbridge devices will be - * redundant and that we will no longer have a 1:1 mapping between the - * northbridge and the data fabric devices. Every data fabric will have a - * northbridge, but not every northbridge will have a data fabric device mapped. - * Because we're always trying to map from a die to a northbridge and not the - * reverse, the fact that there are extra northbridge devices hanging around - * that we don't know about shouldn't be a problem. - * - * ------------------------------- - * Attach and Detach Complications - * ------------------------------- - * - * Because we need to map different PCI devices together, this means that we - * have multiple dev_info_t structures that we need to manage. Each of these is - * independently attached and detached. While this is easily managed for attach, - * it is not for detach. - * - * Once a device has been detached it will only come back if we have an active - * minor node that will be accessed. While we have minor nodes associated with - * the northbridges, we don't with the data fabric devices. This means that if - * they are detached, nothing would ever cause them to be reattached. The system - * also doesn't provide us a way or any guarantees around making sure that we're - * attached to all such devices before we detach. As a result, unfortunately, - * it's easier to basically have detach always fail. - * - * To deal with both development and if issues arise in the field, there is a - * knob, amdf17df_allow_detach, which if set to a non-zero value, will allow - * instances to detach. - * - * --------------- - * Exposed Devices - * --------------- - * - * Currently we expose a single set of character devices which represent - * temperature sensors for this family of processors. Because temperature - * sensors exist on a per-processor node basis, we create a single minor node - * for each one. Because our naming matches the cpuid naming, FMA can match that - * up to logical CPUs and take care of matching the sensors appropriately. We - * internally rate limit the sensor updates to 100ms, which is controlled by the - * global amdf17nbdf_cache_ms. - */ - -#include <sys/modctl.h> -#include <sys/conf.h> -#include <sys/devops.h> -#include <sys/types.h> -#include <sys/file.h> -#include <sys/open.h> -#include <sys/cred.h> -#include <sys/ddi.h> -#include <sys/sunddi.h> -#include <sys/cmn_err.h> -#include <sys/list.h> -#include <sys/pci.h> -#include <sys/stddef.h> -#include <sys/stat.h> -#include <sys/x86_archext.h> -#include <sys/cpuvar.h> -#include <sys/sensors.h> - -/* - * The range of minors that we'll allow. - */ -#define AMDF17_MINOR_LOW 1 -#define AMDF17_MINOR_HIGH INT32_MAX - -/* - * This is the value of the first PCI data fabric device that globally exists. - * It always maps to AMD's first nodeid (what we call cpi_procnodeid). - */ -#define AMDF17_DF_FIRST_DEVICE 0x18 - -/* - * The data fabric devices are defined to always be on PCI bus zero. - */ -#define AMDF17_DF_BUSNO 0x00 - -/* - * This register contains the BUS A of the the processor node that corresponds - * to the data fabric device. - */ -#define AMDF17_DF_CFG_ADDR_CTL 0x84 -#define AMDF17_DF_CFG_ADDR_CTL_MASK 0xff - -/* - * Northbridge registers that are related to accessing the SMN. One writes to - * the SMN address register and then can read from the SMN data register. - */ -#define AMDF17_NB_SMN_ADDR 0x60 -#define AMDF17_NB_SMN_DATA 0x64 - -/* - * The following are register offsets and the meaning of their bits related to - * temperature. These addresses are addresses in the System Management Network - * which is accessed through the northbridge. They are not addresses in PCI - * configuration space. - */ -#define AMDF17_SMU_THERMAL_CURTEMP 0x00059800 -#define AMDF17_SMU_THERMAL_CURTEMP_TEMPERATURE(x) ((x) >> 21) -#define AMDF17_SMU_THERMAL_CURTEMP_RANGE_SEL (1 << 19) - -#define AMDF17_SMU_THERMAL_CURTEMP_RANGE_ADJ (-49) -#define AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS 3 -#define AMDF17_SMU_THERMAL_CURTEMP_BITS_MASK 0x7 - -/* - * The temperature sensor in family 17 is measured in terms of 0.125 C steps. - */ -#define AMDF17_THERMAL_GRANULARITY 8 - -struct amdf17nb; -struct amdf17df; - -typedef struct amdf17nb { - list_node_t amd_nb_link; - dev_info_t *amd_nb_dip; - ddi_acc_handle_t amd_nb_cfgspace; - uint_t amd_nb_bus; - uint_t amd_nb_dev; - uint_t amd_nb_func; - struct amdf17df *amd_nb_df; - uint_t amd_nb_procnodeid; - id_t amd_nb_temp_minor; - hrtime_t amd_nb_temp_last_read; - int amd_nb_temp_off; - uint32_t amd_nb_temp_reg; - /* Values derived from the above */ - int64_t amd_nb_temp; -} amdf17nb_t; - -typedef struct amdf17df { - list_node_t amd_df_link; - dev_info_t *amd_df_f0_dip; - ddi_acc_handle_t amd_df_f0_cfgspace; - uint_t amd_df_procnodeid; - uint_t amd_df_iobus; - amdf17nb_t *amd_df_nb; -} amdf17df_t; - -typedef struct amdf17nbdf { - kmutex_t amd_nbdf_lock; - id_space_t *amd_nbdf_minors; - list_t amd_nbdf_nbs; - list_t amd_nbdf_dfs; -} amdf17nbdf_t; - -typedef enum { - AMD_NBDF_TYPE_UNKNOWN, - AMD_NBDF_TYPE_NORTHBRIDGE, - AMD_NBDF_TYPE_DATA_FABRIC -} amdf17nbdf_type_t; - -typedef struct { - uint16_t amd_nbdft_pci_did; - amdf17nbdf_type_t amd_nbdft_type; -} amdf17nbdf_table_t; - -static const amdf17nbdf_table_t amdf17nbdf_dev_map[] = { - /* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */ - { 0x1450, AMD_NBDF_TYPE_NORTHBRIDGE }, - { 0x1460, AMD_NBDF_TYPE_DATA_FABRIC }, - /* Family 17h Raven Ridge Models 10h-1fh (Zen uarch) */ - { 0x15d0, AMD_NBDF_TYPE_NORTHBRIDGE }, - { 0x15e8, AMD_NBDF_TYPE_DATA_FABRIC }, - /* Family 17h Epyc Models 30h-3fh (Zen 2 uarch) */ - { 0x1480, AMD_NBDF_TYPE_NORTHBRIDGE }, - { 0x1490, AMD_NBDF_TYPE_DATA_FABRIC }, - /* - * Family 17h Ryzen Models 70-7fh (Zen 2 uarch) - * - * While this family has its own PCI ID for the data fabric device, it - * shares the same northbridge ID as the Zen 2 EPYC models 30-3f -- - * 0x1480. - */ - { 0x1440, AMD_NBDF_TYPE_DATA_FABRIC }, - { PCI_EINVAL16 } -}; - -typedef struct { - const char *amd_nbdfo_brand; - uint_t amd_nbdfo_family; - int amd_nbdfo_off; -} amdf17nbdf_offset_t; - -/* - * AMD processors report a control temperature (called Tctl) which may be - * different from the junction temperature, which is the value that is actually - * measured from the die (sometimes called Tdie or Tjct). This is done so that - * socket-based environmental monitoring can be consistent from a platform - * perspective, but doesn't help us. Unfortunately, these values aren't in - * datasheets that we can find, but have been documented partially in a series - * of blog posts by AMD when discussing their 'Ryzen Master' monitoring software - * for Windows. - * - * The brand strings below may contain partial matches such in the Threadripper - * cases so we can match the entire family of processors. The offset value is - * the quantity in degrees that we should adjust Tctl to reach Tdie. - */ -static const amdf17nbdf_offset_t amdf17nbdf_offsets[] = { - { "AMD Ryzen 5 1600X", 0x17, -20 }, - { "AMD Ryzen 7 1700X", 0x17, -20 }, - { "AMD Ryzen 7 1800X", 0x17, -20 }, - { "AMD Ryzen 7 2700X", 0x17, -10 }, - { "AMD Ryzen Threadripper 19", 0x17, -27 }, - { "AMD Ryzen Threadripper 29", 0x17, -27 }, - { NULL } -}; - -/* - * This indicates a number of milliseconds that we should wait between reads. - * This is somewhat arbitrary, but the goal is to reduce cross call activity - * and reflect that the sensor may not update all the time. - */ -uint_t amdf17nbdf_cache_ms = 100; - -/* - * This indicates whether detach is allowed. It is not by default. See the - * theory statement section 'Attach and Detach Complications' for more - * information. - */ -uint_t amdf17nbdf_allow_detach = 0; - -/* - * Global data that we keep regarding the device. - */ -amdf17nbdf_t *amdf17nbdf; - -static amdf17nb_t * -amdf17nbdf_lookup_nb(amdf17nbdf_t *nbdf, minor_t minor) -{ - ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock)); - - if (minor < AMDF17_MINOR_LOW || minor > AMDF17_MINOR_HIGH) { - return (NULL); - } - - for (amdf17nb_t *nb = list_head(&nbdf->amd_nbdf_nbs); nb != NULL; - nb = list_next(&nbdf->amd_nbdf_nbs, nb)) { - if ((id_t)minor == nb->amd_nb_temp_minor) { - return (nb); - } - } - - return (NULL); -} - -static void -amdf17nbdf_cleanup_nb(amdf17nbdf_t *nbdf, amdf17nb_t *nb) -{ - if (nb == NULL) - return; - - ddi_remove_minor_node(nb->amd_nb_dip, NULL); - if (nb->amd_nb_temp_minor > 0) { - id_free(nbdf->amd_nbdf_minors, nb->amd_nb_temp_minor); - } - if (nb->amd_nb_cfgspace != NULL) { - pci_config_teardown(&nb->amd_nb_cfgspace); - } - kmem_free(nb, sizeof (amdf17nb_t)); -} - -static void -amdf17nbdf_cleanup_df(amdf17df_t *df) -{ - if (df == NULL) - return; - - if (df->amd_df_f0_cfgspace != NULL) { - pci_config_teardown(&df->amd_df_f0_cfgspace); - } - kmem_free(df, sizeof (amdf17df_t)); -} - -static int -amdf17nbdf_smn_read(amdf17nbdf_t *nbdf, amdf17nb_t *nb, uint32_t addr, - uint32_t *valp) -{ - VERIFY(MUTEX_HELD(&nbdf->amd_nbdf_lock)); - - pci_config_put32(nb->amd_nb_cfgspace, AMDF17_NB_SMN_ADDR, addr); - *valp = pci_config_get32(nb->amd_nb_cfgspace, AMDF17_NB_SMN_DATA); - - return (0); -} - -static int -amdf17nbdf_temp_read(amdf17nbdf_t *nbdf, amdf17nb_t *nb) -{ - int ret; - uint32_t reg, rawtemp, decimal; - - ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock)); - - /* - * Update the last read time first. Even if this fails, we want to make - * sure that we latch the fact that we tried. - */ - nb->amd_nb_temp_last_read = gethrtime(); - if ((ret = amdf17nbdf_smn_read(nbdf, nb, AMDF17_SMU_THERMAL_CURTEMP, - ®)) != 0) { - return (ret); - } - - nb->amd_nb_temp_reg = reg; - - /* - * Take the primary temperature value and break apart its decimal value - * from its main value. - */ - rawtemp = AMDF17_SMU_THERMAL_CURTEMP_TEMPERATURE(reg); - decimal = rawtemp & AMDF17_SMU_THERMAL_CURTEMP_BITS_MASK; - rawtemp = rawtemp >> AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS; - - if ((reg & AMDF17_SMU_THERMAL_CURTEMP_RANGE_SEL) != 0) { - rawtemp += AMDF17_SMU_THERMAL_CURTEMP_RANGE_ADJ; - } - rawtemp += nb->amd_nb_temp_off; - nb->amd_nb_temp = rawtemp << AMDF17_SMU_THERMAL_CURTEMP_DECIMAL_BITS; - nb->amd_nb_temp += decimal; - - return (0); -} - -static int -amdf17nbdf_temp_init(amdf17nbdf_t *nbdf, amdf17nb_t *nb) -{ - uint_t i, family; - char buf[256]; - - if (cpuid_getbrandstr(CPU, buf, sizeof (buf)) >= sizeof (buf)) { - dev_err(nb->amd_nb_dip, CE_WARN, "!failed to read processor " - "brand string, brand larger than internal buffer"); - return (EOVERFLOW); - } - - family = cpuid_getfamily(CPU); - - for (i = 0; amdf17nbdf_offsets[i].amd_nbdfo_brand != NULL; i++) { - if (family != amdf17nbdf_offsets[i].amd_nbdfo_family) - continue; - if (strncmp(buf, amdf17nbdf_offsets[i].amd_nbdfo_brand, - strlen(amdf17nbdf_offsets[i].amd_nbdfo_brand)) == 0) { - nb->amd_nb_temp_off = - amdf17nbdf_offsets[i].amd_nbdfo_off; - break; - } - } - - return (amdf17nbdf_temp_read(nbdf, nb)); -} - -static amdf17nbdf_type_t -amdf17nbdf_dip_type(uint16_t dev) -{ - uint_t i; - const amdf17nbdf_table_t *tp = amdf17nbdf_dev_map; - - for (i = 0; tp[i].amd_nbdft_pci_did != PCI_EINVAL16; i++) { - if (tp[i].amd_nbdft_pci_did == dev) { - return (tp[i].amd_nbdft_type); - } - } - - return (AMD_NBDF_TYPE_UNKNOWN); -} - -static boolean_t -amdf17nbdf_map(amdf17nbdf_t *nbdf, amdf17nb_t *nb, amdf17df_t *df) -{ - int ret; - char buf[128]; - - ASSERT(MUTEX_HELD(&nbdf->amd_nbdf_lock)); - - /* - * This means that we encountered a duplicate. We're going to stop - * processing, but we're not going to fail its attach at this point. - */ - if (nb->amd_nb_df != NULL) { - dev_err(nb->amd_nb_dip, CE_WARN, "!trying to map NB %u/%u/%u " - "to DF procnode %u, but NB is already mapped to DF " - "procnode %u!", - nb->amd_nb_bus, nb->amd_nb_dev, nb->amd_nb_func, - df->amd_df_procnodeid, nb->amd_nb_df->amd_df_procnodeid); - return (B_TRUE); - } - - /* - * Now that we have found a mapping, initialize our temperature - * information and create the minor node. - */ - nb->amd_nb_procnodeid = df->amd_df_procnodeid; - nb->amd_nb_temp_minor = id_alloc(nbdf->amd_nbdf_minors); - - if ((ret = amdf17nbdf_temp_init(nbdf, nb)) != 0) { - dev_err(nb->amd_nb_dip, CE_WARN, "!failed to init SMN " - "temperature data on node %u: %d", nb->amd_nb_procnodeid, - ret); - return (B_FALSE); - } - - if (snprintf(buf, sizeof (buf), "procnode.%u", nb->amd_nb_procnodeid) >= - sizeof (buf)) { - dev_err(nb->amd_nb_dip, CE_WARN, "!unexpected buffer name " - "overrun assembling temperature minor %u", - nb->amd_nb_procnodeid); - return (B_FALSE); - } - - if (ddi_create_minor_node(nb->amd_nb_dip, buf, S_IFCHR, - nb->amd_nb_temp_minor, DDI_NT_SENSOR_TEMP_CPU, 0) != DDI_SUCCESS) { - dev_err(nb->amd_nb_dip, CE_WARN, "!failed to create minor node " - "%s", buf); - return (B_FALSE); - } - - /* - * Now that's it's all done, note that they're mapped to each other. - */ - nb->amd_nb_df = df; - df->amd_df_nb = nb; - - return (B_TRUE); -} - -static boolean_t -amdf17nbdf_add_nb(amdf17nbdf_t *nbdf, amdf17nb_t *nb) -{ - amdf17df_t *df; - boolean_t ret = B_TRUE; - - mutex_enter(&nbdf->amd_nbdf_lock); - list_insert_tail(&nbdf->amd_nbdf_nbs, nb); - for (df = list_head(&nbdf->amd_nbdf_dfs); df != NULL; - df = list_next(&nbdf->amd_nbdf_dfs, df)) { - if (nb->amd_nb_bus == df->amd_df_iobus) { - ret = amdf17nbdf_map(nbdf, nb, df); - break; - } - } - mutex_exit(&nbdf->amd_nbdf_lock); - - return (ret); -} - -static boolean_t -amdf17nbdf_add_df(amdf17nbdf_t *nbdf, amdf17df_t *df) -{ - amdf17nb_t *nb; - boolean_t ret = B_TRUE; - - mutex_enter(&nbdf->amd_nbdf_lock); - list_insert_tail(&nbdf->amd_nbdf_dfs, df); - for (nb = list_head(&nbdf->amd_nbdf_nbs); nb != NULL; - nb = list_next(&nbdf->amd_nbdf_nbs, nb)) { - if (nb->amd_nb_bus == df->amd_df_iobus) { - ret = amdf17nbdf_map(nbdf, nb, df); - } - } - mutex_exit(&nbdf->amd_nbdf_lock); - - return (ret); -} - -static boolean_t -amdf17nbdf_attach_nb(amdf17nbdf_t *nbdf, dev_info_t *dip, ddi_acc_handle_t hdl, - uint_t bus, uint_t dev, uint_t func) -{ - amdf17nb_t *nb; - - nb = kmem_zalloc(sizeof (amdf17nb_t), KM_SLEEP); - nb->amd_nb_dip = dip; - nb->amd_nb_cfgspace = hdl; - nb->amd_nb_bus = bus; - nb->amd_nb_dev = dev; - nb->amd_nb_func = func; - /* - * Set this to a value we won't get from the processor. - */ - nb->amd_nb_procnodeid = UINT_MAX; - - if (!amdf17nbdf_add_nb(nbdf, nb)) { - amdf17nbdf_cleanup_nb(nbdf, nb); - return (B_FALSE); - } - - return (B_TRUE); -} - -static boolean_t -amdf17nbdf_attach_df(amdf17nbdf_t *nbdf, dev_info_t *dip, ddi_acc_handle_t hdl, - uint_t bus, uint_t dev, uint_t func) -{ - amdf17df_t *df; - - if (bus != AMDF17_DF_BUSNO) { - dev_err(dip, CE_WARN, "!encountered data fabric device with " - "unexpected PCI bus assignment, found 0x%x, expected 0x%x", - bus, AMDF17_DF_BUSNO); - return (B_FALSE); - } - - if (dev < AMDF17_DF_FIRST_DEVICE) { - dev_err(dip, CE_WARN, "!encountered data fabric device with " - "PCI device assignment below the first minimum device " - "(0x%x): 0x%x", AMDF17_DF_FIRST_DEVICE, dev); - return (B_FALSE); - } - - /* - * At the moment we only care about function 0. However, we may care - * about Function 4 in the future which has access to the FICAA. - * However, only function zero should ever be attached, so this is just - * an extra precaution. - */ - if (func != 0) { - dev_err(dip, CE_WARN, "!encountered data fabric device with " - "unxpected PCI function assignment, found 0x%x, expected " - "0x0", func); - return (B_FALSE); - } - - df = kmem_zalloc(sizeof (amdf17df_t), KM_SLEEP); - df->amd_df_f0_dip = dip; - df->amd_df_f0_cfgspace = hdl; - df->amd_df_procnodeid = dev - AMDF17_DF_FIRST_DEVICE; - df->amd_df_iobus = pci_config_get32(hdl, AMDF17_DF_CFG_ADDR_CTL) & - AMDF17_DF_CFG_ADDR_CTL_MASK; - - if (!amdf17nbdf_add_df(nbdf, df)) { - amdf17nbdf_cleanup_df(df); - return (B_FALSE); - } - - return (B_TRUE); -} - -static int -amdf17nbdf_open(dev_t *devp, int flags, int otype, cred_t *credp) -{ - amdf17nbdf_t *nbdf = amdf17nbdf; - minor_t m; - - if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) { - return (EPERM); - } - - if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) { - return (EINVAL); - } - - if (otype != OTYP_CHR) { - return (EINVAL); - } - - m = getminor(*devp); - - /* - * Sanity check the minor - */ - mutex_enter(&nbdf->amd_nbdf_lock); - if (amdf17nbdf_lookup_nb(nbdf, m) == NULL) { - mutex_exit(&nbdf->amd_nbdf_lock); - return (ENXIO); - } - mutex_exit(&nbdf->amd_nbdf_lock); - - return (0); -} - -static int -amdf17nbdf_ioctl_kind(intptr_t arg, int mode) -{ - sensor_ioctl_kind_t kind; - - bzero(&kind, sizeof (sensor_ioctl_kind_t)); - kind.sik_kind = SENSOR_KIND_TEMPERATURE; - - if (ddi_copyout((void *)&kind, (void *)arg, - sizeof (sensor_ioctl_kind_t), mode & FKIOCTL) != 0) { - return (EFAULT); - } - - return (0); -} - -static int -amdf17nbdf_ioctl_scalar(amdf17nbdf_t *nbdf, minor_t minor, intptr_t arg, - int mode) -{ - amdf17nb_t *nb; - hrtime_t diff; - sensor_ioctl_scalar_t scalar; - - bzero(&scalar, sizeof (scalar)); - - mutex_enter(&nbdf->amd_nbdf_lock); - nb = amdf17nbdf_lookup_nb(nbdf, minor); - if (nb == NULL) { - mutex_exit(&nbdf->amd_nbdf_lock); - return (ENXIO); - } - - diff = NSEC2MSEC(gethrtime() - nb->amd_nb_temp_last_read); - if (diff > 0 && diff > (hrtime_t)amdf17nbdf_cache_ms) { - int ret; - - ret = amdf17nbdf_temp_read(nbdf, nb); - if (ret != 0) { - mutex_exit(&nbdf->amd_nbdf_lock); - return (ret); - } - } - - scalar.sis_unit = SENSOR_UNIT_CELSIUS; - scalar.sis_value = nb->amd_nb_temp; - scalar.sis_gran = AMDF17_THERMAL_GRANULARITY; - mutex_exit(&nbdf->amd_nbdf_lock); - - if (ddi_copyout(&scalar, (void *)arg, sizeof (scalar), - mode & FKIOCTL) != 0) { - return (EFAULT); - } - - return (0); -} - -static int -amdf17nbdf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, - int *rvalp) -{ - minor_t m; - amdf17nbdf_t *nbdf = amdf17nbdf; - - if ((mode & FREAD) == 0) { - return (EINVAL); - } - - m = getminor(dev); - - switch (cmd) { - case SENSOR_IOCTL_KIND: - return (amdf17nbdf_ioctl_kind(arg, mode)); - case SENSOR_IOCTL_SCALAR: - return (amdf17nbdf_ioctl_scalar(nbdf, m, arg, mode)); - default: - return (ENOTTY); - } -} - -/* - * We don't really do any state tracking on close, so for now, just allow it to - * always succeed. - */ -static int -amdf17nbdf_close(dev_t dev, int flags, int otype, cred_t *credp) -{ - return (0); -} - -static int -amdf17nbdf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) -{ - uint_t nregs; - int *regs; - uint_t bus, dev, func; - uint16_t pci_did; - ddi_acc_handle_t pci_hdl; - amdf17nbdf_type_t type; - amdf17nbdf_t *nbdf = amdf17nbdf; - - if (cmd == DDI_RESUME) - return (DDI_SUCCESS); - if (cmd != DDI_ATTACH) - return (DDI_FAILURE); - - if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0, "reg", - ®s, &nregs) != DDI_PROP_SUCCESS) { - dev_err(dip, CE_WARN, "!failed to find pci 'reg' property"); - return (DDI_FAILURE); - } - - if (nregs < 1) { - ddi_prop_free(regs); - return (DDI_FAILURE); - } - - bus = PCI_REG_BUS_G(regs[0]); - dev = PCI_REG_DEV_G(regs[0]); - func = PCI_REG_FUNC_G(regs[0]); - - ddi_prop_free(regs); - - if (pci_config_setup(dip, &pci_hdl) != DDI_SUCCESS) { - dev_err(dip, CE_WARN, "!failed to map pci devices"); - return (DDI_FAILURE); - } - - pci_did = pci_config_get16(pci_hdl, PCI_CONF_DEVID); - - type = amdf17nbdf_dip_type(pci_did); - switch (type) { - case AMD_NBDF_TYPE_NORTHBRIDGE: - if (!amdf17nbdf_attach_nb(nbdf, dip, pci_hdl, bus, dev, func)) { - return (DDI_FAILURE); - } - break; - case AMD_NBDF_TYPE_DATA_FABRIC: - if (!amdf17nbdf_attach_df(nbdf, dip, pci_hdl, bus, dev, func)) { - return (DDI_FAILURE); - } - break; - default: - pci_config_teardown(&pci_hdl); - return (DDI_FAILURE); - } - - return (DDI_SUCCESS); -} - -/* - * Unfortunately, it's hard for us to really support detach here. The problem is - * that we need both the data fabric devices and the northbridges to make sure - * that we map everything. However, only the northbridges actually create minor - * nodes that'll be opened and thus trigger them to reattach when accessed. What - * we should probably look at doing in the future is making this into a nexus - * driver that enumerates children like a temperature driver. - */ -static int -amdf17nbdf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) -{ - amdf17nbdf_t *nbdf = amdf17nbdf; - - if (cmd == DDI_SUSPEND) - return (DDI_SUCCESS); - - if (nbdf == NULL) { - return (DDI_FAILURE); - } - - if (amdf17nbdf_allow_detach == 0) { - return (DDI_FAILURE); - } - - mutex_enter(&nbdf->amd_nbdf_lock); - for (amdf17nb_t *nb = list_head(&nbdf->amd_nbdf_nbs); nb != NULL; - nb = list_next(&nbdf->amd_nbdf_nbs, nb)) { - if (dip == nb->amd_nb_dip) { - list_remove(&nbdf->amd_nbdf_nbs, nb); - if (nb->amd_nb_df != NULL) { - ASSERT3P(nb->amd_nb_df->amd_df_nb, ==, nb); - nb->amd_nb_df->amd_df_nb = NULL; - } - amdf17nbdf_cleanup_nb(nbdf, nb); - mutex_exit(&nbdf->amd_nbdf_lock); - return (DDI_SUCCESS); - } - } - - for (amdf17df_t *df = list_head(&nbdf->amd_nbdf_dfs); df != NULL; - df = list_next(&nbdf->amd_nbdf_nbs, df)) { - if (dip == df->amd_df_f0_dip) { - list_remove(&nbdf->amd_nbdf_dfs, df); - if (df->amd_df_nb != NULL) { - ASSERT3P(df->amd_df_nb->amd_nb_df, ==, df); - df->amd_df_nb->amd_nb_df = NULL; - } - amdf17nbdf_cleanup_df(df); - mutex_exit(&nbdf->amd_nbdf_lock); - return (DDI_SUCCESS); - } - } - mutex_exit(&nbdf->amd_nbdf_lock); - - return (DDI_FAILURE); -} - -static int -amdf17nbdf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, - void **resultp) -{ - dev_t dev; - minor_t minor; - amdf17nbdf_t *nbdf; - amdf17nb_t *nb; - - switch (cmd) { - case DDI_INFO_DEVT2DEVINFO: - case DDI_INFO_DEVT2INSTANCE: - break; - default: - return (DDI_FAILURE); - } - - dev = (dev_t)arg; - minor = getminor(dev); - nbdf = amdf17nbdf; - - mutex_enter(&nbdf->amd_nbdf_lock); - nb = amdf17nbdf_lookup_nb(nbdf, (id_t)minor); - if (nb == NULL) { - mutex_exit(&nbdf->amd_nbdf_lock); - return (DDI_FAILURE); - } - if (cmd == DDI_INFO_DEVT2DEVINFO) { - *resultp = nb->amd_nb_dip; - } else { - int inst = ddi_get_instance(nb->amd_nb_dip); - *resultp = (void *)(uintptr_t)inst; - } - mutex_exit(&nbdf->amd_nbdf_lock); - - return (DDI_SUCCESS); -} - -static void -amdf17nbdf_destroy(amdf17nbdf_t *nbdf) -{ - amdf17nb_t *nb; - amdf17df_t *df; - - while ((nb = list_remove_head(&nbdf->amd_nbdf_nbs)) != NULL) { - amdf17nbdf_cleanup_nb(nbdf, nb); - } - list_destroy(&nbdf->amd_nbdf_nbs); - - while ((df = list_remove_head(&nbdf->amd_nbdf_dfs)) != NULL) { - amdf17nbdf_cleanup_df(df); - } - list_destroy(&nbdf->amd_nbdf_dfs); - - if (nbdf->amd_nbdf_minors != NULL) { - id_space_destroy(nbdf->amd_nbdf_minors); - } - - mutex_destroy(&nbdf->amd_nbdf_lock); - kmem_free(nbdf, sizeof (amdf17nbdf_t)); -} - -static amdf17nbdf_t * -amdf17nbdf_create(void) -{ - amdf17nbdf_t *nbdf; - - nbdf = kmem_zalloc(sizeof (amdf17nbdf_t), KM_SLEEP); - mutex_init(&nbdf->amd_nbdf_lock, NULL, MUTEX_DRIVER, NULL); - list_create(&nbdf->amd_nbdf_nbs, sizeof (amdf17nb_t), - offsetof(amdf17nb_t, amd_nb_link)); - list_create(&nbdf->amd_nbdf_dfs, sizeof (amdf17df_t), - offsetof(amdf17df_t, amd_df_link)); - if ((nbdf->amd_nbdf_minors = id_space_create("amdf17nbdf_minors", - AMDF17_MINOR_LOW, AMDF17_MINOR_HIGH)) == NULL) { - amdf17nbdf_destroy(nbdf); - return (NULL); - } - - return (nbdf); -} - -static struct cb_ops amdf17nbdf_cb_ops = { - .cb_open = amdf17nbdf_open, - .cb_close = amdf17nbdf_close, - .cb_strategy = nodev, - .cb_print = nodev, - .cb_dump = nodev, - .cb_read = nodev, - .cb_write = nodev, - .cb_ioctl = amdf17nbdf_ioctl, - .cb_devmap = nodev, - .cb_mmap = nodev, - .cb_segmap = nodev, - .cb_chpoll = nochpoll, - .cb_prop_op = ddi_prop_op, - .cb_flag = D_MP, - .cb_rev = CB_REV, - .cb_aread = nodev, - .cb_awrite = nodev -}; - -static struct dev_ops amdf17nbdf_dev_ops = { - .devo_rev = DEVO_REV, - .devo_refcnt = 0, - .devo_getinfo = amdf17nbdf_getinfo, - .devo_identify = nulldev, - .devo_probe = nulldev, - .devo_attach = amdf17nbdf_attach, - .devo_detach = amdf17nbdf_detach, - .devo_reset = nodev, - .devo_quiesce = ddi_quiesce_not_needed, - .devo_cb_ops = &amdf17nbdf_cb_ops -}; - -static struct modldrv amdf17nbdf_modldrv = { - .drv_modops = &mod_driverops, - .drv_linkinfo = "AMD Family 17h Driver", - .drv_dev_ops = &amdf17nbdf_dev_ops -}; - -static struct modlinkage amdf17nbdf_modlinkage = { - .ml_rev = MODREV_1, - .ml_linkage = { &amdf17nbdf_modldrv, NULL } -}; - -int -_init(void) -{ - int ret; - amdf17nbdf_t *nbdf; - - if ((nbdf = amdf17nbdf_create()) == NULL) { - return (ENOMEM); - } - - if ((ret = mod_install(&amdf17nbdf_modlinkage)) != 0) { - amdf17nbdf_destroy(amdf17nbdf); - return (ret); - } - - amdf17nbdf = nbdf; - return (ret); -} - -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&amdf17nbdf_modlinkage, modinfop)); -} - -int -_fini(void) -{ - int ret; - - if ((ret = mod_remove(&amdf17nbdf_modlinkage)) != 0) { - return (ret); - } - - amdf17nbdf_destroy(amdf17nbdf); - amdf17nbdf = NULL; - return (ret); -} diff --git a/usr/src/uts/intel/io/amdzen/amdzen.c b/usr/src/uts/intel/io/amdzen/amdzen.c new file mode 100644 index 0000000000..25e38ee988 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/amdzen.c @@ -0,0 +1,1016 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019, Joyent, Inc. + * Copyright 2020 Oxide Computer Company + */ + +/* + * Nexus Driver for AMD Zen family systems. The purpose of this driver is to + * provide access to the following resources in a single, centralized fashion: + * + * - The per-chip Data Fabric + * - The North Bridge + * - The System Management Network (SMN) + * + * This is a nexus driver as once we have attached to all the requisite + * components, we will enumerate child devices which consume this functionality. + * + * ------------------------ + * Mapping Devices Together + * ------------------------ + * + * The operating system needs to expose things like temperature sensors and DRAM + * configuration registers in terms that are meaningful to the system such as + * logical CPUs, cores, etc. This driver attaches to the PCI IDs that represent + * the northbridge and data fabric; however, there are multiple PCI devices (one + * per die) that exist. This driver does manage to map all of these three things + * together; however, it requires some acrobatics. Unfortunately, there's no + * direct way to map a northbridge to its corresponding die. However, we can map + * a CPU die to a data fabric PCI device and a data fabric PCI device to a + * corresponding northbridge PCI device. + * + * In current Zen based products, there is a direct mapping between processor + * nodes and a data fabric PCI device. All of the devices are on PCI Bus 0 and + * start from Device 0x18. Device 0x18 maps to processor node 0, 0x19 to + * processor node 1, etc. This means that to map a logical CPU to a data fabric + * device, we take its processor node id, add it to 0x18 and find the PCI device + * that is on bus 0, device 0x18. As each data fabric device is attached based + * on its PCI ID, we add it to the global list, amd_nbdf_dfs that is in the + * amd_f17nbdf_t structure. + * + * The northbridge PCI device has a defined device and function, but the PCI bus + * that it's on can vary. Each die has its own series of PCI buses that are + * assigned to it and the northbridge PCI device is on the first of die-specific + * PCI bus for each die. This also means that the northbridge will not show up + * on PCI bus 0, which is the PCI bus that all of the data fabric devices are + * on. While conventionally the northbridge with the lowest PCI bus value + * would correspond to processor node zero, hardware does not guarantee that at + * all. Because we don't want to be at the mercy of firmware, we don't rely on + * this ordering, even though we have yet to find a system that deviates from + * this scheme. + * + * One of the registers in the data fabric device's function 0 + * (AMDZEN_DF_F0_CFG_ADDR_CTL) happens to have the first PCI bus that is + * associated with the processor node. This means that we can map a data fabric + * device to a northbridge by finding the northbridge whose PCI bus matches the + * value in the corresponding data fabric's AMDZEN_DF_F0_CFG_ADDR_CTL. + * + * We can map a northbridge to a data fabric device and a data fabric device to + * a die. Because these are generally 1:1 mappings, there is a transitive + * relationship and therefore we know which northbridge is associated with which + * processor die. This is summarized in the following image: + * + * +-------+ +-----------------------------------+ +--------------+ + * | Die 0 |--->| Data Fabric PCI BDF 0/18/0 |------->| Northbridge | + * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 10 | | PCI 10/0/0 | + * ... +-----------------------------------+ +--------------+ + * +-------+ +------------------------------------+ +--------------+ + * | Die n |---->| Data Fabric PCI BDF 0/18+n/0 |------->| Northbridge | + * +-------+ | AMDZEN_DF_F0_CFG_ADDR_CTL: bus 133 | | PCI 133/0/0 | + * +------------------------------------+ +--------------+ + * + * Note, the PCI buses used by the northbridges here are arbitrary. They do not + * reflect the actual values by hardware; however, the bus/device/function (BDF) + * of the data fabric accurately models hardware. All of the BDF values are in + * hex. + * + * Starting with the Rome generation of processors (Family 17h Model 30-3Fh), + * AMD has multiple northbridges that exist on a given die. All of these + * northbridges share the same data fabric and system management network port. + * From our perspective this means that some of the northbridge devices will be + * redundant and that we will no longer have a 1:1 mapping between the + * northbridge and the data fabric devices. Every data fabric will have a + * northbridge, but not every northbridge will have a data fabric device mapped. + * Because we're always trying to map from a die to a northbridge and not the + * reverse, the fact that there are extra northbridge devices hanging around + * that we don't know about shouldn't be a problem. + * + * ------------------------------- + * Attach and Detach Complications + * ------------------------------- + * + * Because we need to map different PCI devices together, this means that we + * have multiple dev_info_t structures that we need to manage. Each of these is + * independently attached and detached. While this is easily managed for attach, + * it is not for detach. Each of these devices is a 'stub'. + * + * Once a device has been detached it will only come back if we have an active + * minor node that will be accessed. This means that if they are detached, + * nothing would ever cause them to be reattached. The system also doesn't + * provide us a way or any guarantees around making sure that we're attached to + * all such devices before we detach. As a result, unfortunately, it's easier to + * basically have detach always fail. + * + * --------------- + * Exposed Devices + * --------------- + * + * Rather than try and have all of the different functions that could be + * provided by one driver, we instead have created a nexus driver that will + * itself try and load children. Children are all pseudo-device drivers that + * provide different pieces of functionality that use this. + */ + +#include <sys/modctl.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/pci.h> +#include <sys/sysmacros.h> +#include <sys/sunndi.h> +#include <sys/x86_archext.h> +#include <sys/cpuvar.h> + +#include "amdzen.h" + +amdzen_t *amdzen_data; + +/* + * Array of northbridge IDs that we care about. + */ +static const uint16_t amdzen_nb_ids[] = { + /* Family 17h Ryzen, Epyc Models 00h-0fh (Zen uarch) */ + 0x1450, + /* Family 17h Raven Ridge, Kestrel, Dali Models 10h-2fh (Zen uarch) */ + 0x15d0, + /* Family 17h Epyc Models 30h-3fh, Matisse 70-7fh (Zen 2 uarch) */ + 0x1480, + /* Family 17h Renoir Models 60-6fh (Zen 2 uarch) */ + 0x1630 +}; + +typedef struct { + char *acd_name; + amdzen_child_t acd_addr; +} amdzen_child_data_t; + +static const amdzen_child_data_t amdzen_children[] = { + { "smntemp", AMDZEN_C_SMNTEMP }, + { "usmn", AMDZEN_C_USMN }, + { "zen_udf", AMDZEN_C_ZEN_UDF } +}; + +static uint32_t +amdzen_stub_get32(amdzen_stub_t *stub, off_t reg) +{ + return (pci_config_get32(stub->azns_cfgspace, reg)); +} + +static uint64_t +amdzen_stub_get64(amdzen_stub_t *stub, off_t reg) +{ + return (pci_config_get64(stub->azns_cfgspace, reg)); +} + +static void +amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val) +{ + pci_config_put32(stub->azns_cfgspace, reg, val); +} + +/* + * Perform a targeted 32-bit indirect read to a specific instance and function. + */ +static uint32_t +amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func, + uint16_t reg) +{ + uint32_t val; + + VERIFY(MUTEX_HELD(&azn->azn_mutex)); + val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) | + AMDZEN_DF_F4_FICAA_SET_FUNC(func) | + AMDZEN_DF_F4_FICAA_SET_INST(inst); + amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val); + return (amdzen_stub_get32(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO)); +} + +/* + * Perform a targeted 64-bit indirect read to a specific instance and function. + */ +static uint64_t +amdzen_df_read64(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func, + uint16_t reg) +{ + uint32_t val; + + VERIFY(MUTEX_HELD(&azn->azn_mutex)); + val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) | + AMDZEN_DF_F4_FICAA_SET_FUNC(func) | + AMDZEN_DF_F4_FICAA_SET_INST(inst) | AMDZEN_DF_F4_FICAA_SET_64B; + amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val); + return (amdzen_stub_get64(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO)); +} + + +static uint32_t +amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, uint32_t reg) +{ + VERIFY(MUTEX_HELD(&azn->azn_mutex)); + amdzen_stub_put32(df->adf_nb, AMDZEN_NB_SMN_ADDR, reg); + return (amdzen_stub_get32(df->adf_nb, AMDZEN_NB_SMN_DATA)); +} + +static amdzen_df_t * +amdzen_df_find(amdzen_t *azn, uint_t dfno) +{ + uint_t i; + + ASSERT(MUTEX_HELD(&azn->azn_mutex)); + if (dfno >= azn->azn_ndfs) { + return (NULL); + } + + for (i = 0; i < azn->azn_ndfs; i++) { + amdzen_df_t *df = &azn->azn_dfs[i]; + if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) { + continue; + } + + if (dfno == 0) { + return (df); + } + dfno--; + } + + return (NULL); +} + +/* + * Client functions that are used by nexus children. + */ +int +amdzen_c_smn_read32(uint_t dfno, uint32_t reg, uint32_t *valp) +{ + amdzen_df_t *df; + amdzen_t *azn = amdzen_data; + + mutex_enter(&azn->azn_mutex); + df = amdzen_df_find(azn, dfno); + if (df == NULL) { + mutex_exit(&azn->azn_mutex); + return (ENOENT); + } + + if ((df->adf_flags & AMDZEN_DF_F_FOUND_NB) == 0) { + mutex_exit(&azn->azn_mutex); + return (ENXIO); + } + + *valp = amdzen_smn_read32(azn, df, reg); + mutex_exit(&azn->azn_mutex); + return (0); +} + +uint_t +amdzen_c_df_count(void) +{ + uint_t ret; + amdzen_t *azn = amdzen_data; + + mutex_enter(&azn->azn_mutex); + ret = azn->azn_ndfs; + mutex_exit(&azn->azn_mutex); + return (ret); +} + +int +amdzen_c_df_read32(uint_t dfno, uint8_t inst, uint8_t func, + uint16_t reg, uint32_t *valp) +{ + amdzen_df_t *df; + amdzen_t *azn = amdzen_data; + + mutex_enter(&azn->azn_mutex); + df = amdzen_df_find(azn, dfno); + if (df == NULL) { + mutex_exit(&azn->azn_mutex); + return (ENOENT); + } + + *valp = amdzen_df_read32(azn, df, inst, func, reg); + mutex_exit(&azn->azn_mutex); + + return (0); +} + +int +amdzen_c_df_read64(uint_t dfno, uint8_t inst, uint8_t func, + uint16_t reg, uint64_t *valp) +{ + amdzen_df_t *df; + amdzen_t *azn = amdzen_data; + + mutex_enter(&azn->azn_mutex); + df = amdzen_df_find(azn, dfno); + if (df == NULL) { + mutex_exit(&azn->azn_mutex); + return (ENOENT); + } + + *valp = amdzen_df_read64(azn, df, inst, func, reg); + mutex_exit(&azn->azn_mutex); + + return (0); +} + +static boolean_t +amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd) +{ + int ret; + dev_info_t *child; + + if (ndi_devi_alloc(azn->azn_dip, acd->acd_name, + (pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) { + dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child " + "dip for %s", acd->acd_name); + return (B_FALSE); + } + + ddi_set_parent_data(child, (void *)acd); + if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) { + dev_err(azn->azn_dip, CE_WARN, "!failed to online child " + "dip %s: %d", acd->acd_name, ret); + return (B_FALSE); + } + + return (B_TRUE); +} + +static boolean_t +amdzen_map_dfs(amdzen_t *azn) +{ + amdzen_stub_t *stub; + + ASSERT(MUTEX_HELD(&azn->azn_mutex)); + + for (stub = list_head(&azn->azn_df_stubs); stub != NULL; + stub = list_next(&azn->azn_df_stubs, stub)) { + amdzen_df_t *df; + uint_t dfno; + + dfno = stub->azns_dev - AMDZEN_DF_FIRST_DEVICE; + if (dfno > AMDZEN_MAX_DFS) { + dev_err(stub->azns_dip, CE_WARN, "encountered df " + "device with illegal DF PCI b/d/f: 0x%x/%x/%x", + stub->azns_bus, stub->azns_dev, stub->azns_func); + goto err; + } + + df = &azn->azn_dfs[dfno]; + + if (stub->azns_func >= AMDZEN_MAX_DF_FUNCS) { + dev_err(stub->azns_dip, CE_WARN, "encountered df " + "device with illegal DF PCI b/d/f: 0x%x/%x/%x", + stub->azns_bus, stub->azns_dev, stub->azns_func); + goto err; + } + + if (df->adf_funcs[stub->azns_func] != NULL) { + dev_err(stub->azns_dip, CE_WARN, "encountered " + "duplicate df device with DF PCI b/d/f: 0x%x/%x/%x", + stub->azns_bus, stub->azns_dev, stub->azns_func); + goto err; + } + df->adf_funcs[stub->azns_func] = stub; + } + + return (B_TRUE); + +err: + azn->azn_flags |= AMDZEN_F_DEVICE_ERROR; + return (B_FALSE); +} + +static boolean_t +amdzen_check_dfs(amdzen_t *azn) +{ + uint_t i; + boolean_t ret = B_TRUE; + + for (i = 0; i < AMDZEN_MAX_DFS; i++) { + amdzen_df_t *df = &azn->azn_dfs[i]; + uint_t count = 0; + + /* + * We require all platforms to have DFs functions 0-6. Not all + * platforms have DF function 7. + */ + for (uint_t func = 0; func < AMDZEN_MAX_DF_FUNCS - 1; func++) { + if (df->adf_funcs[func] != NULL) { + count++; + } + } + + if (count == 0) + continue; + + if (count != 7) { + ret = B_FALSE; + dev_err(azn->azn_dip, CE_WARN, "df %u devices " + "incomplete", i); + } else { + df->adf_flags |= AMDZEN_DF_F_VALID; + azn->azn_ndfs++; + } + } + + return (ret); +} + +static const uint8_t amdzen_df_rome_ids[0x2b] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48 +}; + +/* + * Initialize our knowledge about a given series of nodes on the data fabric. + */ +static void +amdzen_setup_df(amdzen_t *azn, amdzen_df_t *df) +{ + uint_t i; + uint32_t val; + + val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_CFG_ADDR_CTL); + df->adf_nb_busno = AMDZEN_DF_F0_CFG_ADDR_CTL_BUS_NUM(val); + val = amdzen_stub_get32(df->adf_funcs[0], AMDZEN_DF_F0_FBICNT); + df->adf_nents = AMDZEN_DF_F0_FBICNT_COUNT(val); + if (df->adf_nents == 0) + return; + df->adf_ents = kmem_zalloc(sizeof (amdzen_df_ent_t) * df->adf_nents, + KM_SLEEP); + + for (i = 0; i < df->adf_nents; i++) { + amdzen_df_ent_t *dfe = &df->adf_ents[i]; + uint8_t inst = i; + + /* + * Unfortunately, Rome uses a discontinuous instance ID pattern + * while everything else we can find uses a contiguous instance + * ID pattern. This means that for Rome, we need to adjust the + * indexes that we iterate over, though the total number of + * entries is right. + */ + if (df->adf_funcs[0]->azns_did == 0x1490) { + if (inst > ARRAY_SIZE(amdzen_df_rome_ids)) { + dev_err(azn->azn_dip, CE_WARN, "Rome family " + "processor reported more ids than the PPR, " + "resting %u to instance zero", inst); + inst = 0; + } else { + inst = amdzen_df_rome_ids[inst]; + } + } + + dfe->adfe_drvid = inst; + dfe->adfe_info0 = amdzen_df_read32(azn, df, inst, 0, + AMDZEN_DF_F0_FBIINFO0); + dfe->adfe_info1 = amdzen_df_read32(azn, df, inst, 0, + AMDZEN_DF_F0_FBIINFO1); + dfe->adfe_info2 = amdzen_df_read32(azn, df, inst, 0, + AMDZEN_DF_F0_FBIINFO2); + dfe->adfe_info3 = amdzen_df_read32(azn, df, inst, 0, + AMDZEN_DF_F0_FBIINFO3); + dfe->adfe_syscfg = amdzen_df_read32(azn, df, inst, 1, + AMDZEN_DF_F1_SYSCFG); + dfe->adfe_mask0 = amdzen_df_read32(azn, df, inst, 1, + AMDZEN_DF_F1_FIDMASK0); + dfe->adfe_mask1 = amdzen_df_read32(azn, df, inst, 1, + AMDZEN_DF_F1_FIDMASK1); + + dfe->adfe_type = AMDZEN_DF_F0_FBIINFO0_TYPE(dfe->adfe_info0); + dfe->adfe_sdp_width = + AMDZEN_DF_F0_FBIINFO0_SDP_WIDTH(dfe->adfe_info0); + if (AMDZEN_DF_F0_FBIINFO0_ENABLED(dfe->adfe_info0)) { + dfe->adfe_flags |= AMDZEN_DFE_F_ENABLED; + } + dfe->adfe_fti_width = + AMDZEN_DF_F0_FBIINFO0_FTI_WIDTH(dfe->adfe_info0); + dfe->adfe_sdp_count = + AMDZEN_DF_F0_FBIINFO0_SDP_PCOUNT(dfe->adfe_info0); + dfe->adfe_fti_count = + AMDZEN_DF_F0_FBIINFO0_FTI_PCOUNT(dfe->adfe_info0); + if (AMDZEN_DF_F0_FBIINFO0_HAS_MCA(dfe->adfe_info0)) { + dfe->adfe_flags |= AMDZEN_DFE_F_MCA; + } + dfe->adfe_subtype = + AMDZEN_DF_F0_FBIINFO0_SUBTYPE(dfe->adfe_info0); + + dfe->adfe_inst_id = + AMDZEN_DF_F0_FBIINFO3_INSTID(dfe->adfe_info3); + dfe->adfe_fabric_id = + AMDZEN_DF_F0_FBIINFO3_FABID(dfe->adfe_info3); + } + + df->adf_syscfg = amdzen_stub_get32(df->adf_funcs[1], + AMDZEN_DF_F1_SYSCFG); + df->adf_nodeid = AMDZEN_DF_F1_SYSCFG_NODEID(df->adf_syscfg); + df->adf_mask0 = amdzen_stub_get32(df->adf_funcs[1], + AMDZEN_DF_F1_FIDMASK0); + df->adf_mask1 = amdzen_stub_get32(df->adf_funcs[1], + AMDZEN_DF_F1_FIDMASK1); +} + +static void +amdzen_find_nb(amdzen_t *azn, amdzen_df_t *df) +{ + amdzen_stub_t *stub; + + for (stub = list_head(&azn->azn_nb_stubs); stub != NULL; + stub = list_next(&azn->azn_nb_stubs, stub)) { + if (stub->azns_bus == df->adf_nb_busno) { + df->adf_flags |= AMDZEN_DF_F_FOUND_NB; + df->adf_nb = stub; + return; + } + } +} + +static void +amdzen_nexus_init(void *arg) +{ + uint_t i; + amdzen_t *azn = arg; + + /* + * First go through all of the stubs and assign the DF entries. + */ + mutex_enter(&azn->azn_mutex); + if (!amdzen_map_dfs(azn) || !amdzen_check_dfs(azn)) { + azn->azn_flags |= AMDZEN_F_MAP_ERROR; + goto done; + } + + for (i = 0; i < AMDZEN_MAX_DFS; i++) { + amdzen_df_t *df = &azn->azn_dfs[i]; + + if ((df->adf_flags & AMDZEN_DF_F_VALID) == 0) + continue; + amdzen_setup_df(azn, df); + amdzen_find_nb(azn, df); + } + + /* + * Not all children may be installed. As such, we do not treat the + * failure of a child as fatal to the driver. + */ + mutex_exit(&azn->azn_mutex); + for (i = 0; i < ARRAY_SIZE(amdzen_children); i++) { + (void) amdzen_create_child(azn, &amdzen_children[i]); + } + mutex_enter(&azn->azn_mutex); + +done: + azn->azn_flags &= ~AMDZEN_F_ATTACH_DISPATCHED; + azn->azn_flags |= AMDZEN_F_ATTACH_COMPLETE; + azn->azn_taskqid = TASKQID_INVALID; + cv_broadcast(&azn->azn_cv); + mutex_exit(&azn->azn_mutex); +} + +static int +amdzen_stub_scan_cb(dev_info_t *dip, void *arg) +{ + amdzen_t *azn = arg; + uint16_t vid, did; + int *regs; + uint_t nregs, i; + boolean_t match = B_FALSE; + + if (dip == ddi_root_node()) { + return (DDI_WALK_CONTINUE); + } + + /* + * If a node in question is not a pci node, then we have no interest in + * it as all the stubs that we care about are related to pci devices. + */ + if (strncmp("pci", ddi_get_name(dip), 3) != 0) { + return (DDI_WALK_PRUNECHILD); + } + + /* + * If we can't get a device or vendor ID and prove that this is an AMD + * part, then we don't care about it. + */ + vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "vendor-id", PCI_EINVAL16); + did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "device-id", PCI_EINVAL16); + if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { + return (DDI_WALK_CONTINUE); + } + + if (vid != AMDZEN_PCI_VID_AMD) { + return (DDI_WALK_CONTINUE); + } + + for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { + if (amdzen_nb_ids[i] == did) { + match = B_TRUE; + } + } + + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { + return (DDI_WALK_CONTINUE); + } + + if (nregs == 0) { + ddi_prop_free(regs); + return (DDI_WALK_CONTINUE); + } + + if (PCI_REG_BUS_G(regs[0]) == AMDZEN_DF_BUSNO && + PCI_REG_DEV_G(regs[0]) >= AMDZEN_DF_FIRST_DEVICE) { + match = B_TRUE; + } + + ddi_prop_free(regs); + if (match) { + mutex_enter(&azn->azn_mutex); + azn->azn_nscanned++; + mutex_exit(&azn->azn_mutex); + } + + return (DDI_WALK_CONTINUE); +} + +static void +amdzen_stub_scan(void *arg) +{ + amdzen_t *azn = arg; + + mutex_enter(&azn->azn_mutex); + azn->azn_nscanned = 0; + mutex_exit(&azn->azn_mutex); + + ddi_walk_devs(ddi_root_node(), amdzen_stub_scan_cb, azn); + + mutex_enter(&azn->azn_mutex); + azn->azn_flags &= ~AMDZEN_F_SCAN_DISPATCHED; + azn->azn_flags |= AMDZEN_F_SCAN_COMPLETE; + + if (azn->azn_nscanned == 0) { + azn->azn_flags |= AMDZEN_F_UNSUPPORTED; + azn->azn_taskqid = TASKQID_INVALID; + cv_broadcast(&azn->azn_cv); + } else if (azn->azn_npresent == azn->azn_nscanned) { + azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; + azn->azn_taskqid = taskq_dispatch(system_taskq, + amdzen_nexus_init, azn, TQ_SLEEP); + } + mutex_exit(&azn->azn_mutex); +} + +/* + * Unfortunately we can't really let the stubs detach as we may need them to be + * available for client operations. We may be able to improve this if we know + * that the actual nexus is going away. However, as long as it's active, we need + * all the stubs. + */ +int +amdzen_detach_stub(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd == DDI_SUSPEND) { + return (DDI_SUCCESS); + } + + return (DDI_FAILURE); +} + +int +amdzen_attach_stub(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int *regs, reg; + uint_t nregs, i; + uint16_t vid, did; + amdzen_stub_t *stub; + amdzen_t *azn = amdzen_data; + boolean_t valid = B_FALSE; + boolean_t nb = B_FALSE; + + if (cmd == DDI_RESUME) { + return (DDI_SUCCESS); + } else if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + /* + * Make sure that the stub that we've been asked to attach is a pci type + * device. If not, then there is no reason for us to proceed. + */ + if (strncmp("pci", ddi_get_name(dip), 3) != 0) { + dev_err(dip, CE_WARN, "asked to attach a bad AMD Zen nexus " + "stub: %s", ddi_get_name(dip)); + return (DDI_FAILURE); + } + vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "vendor-id", PCI_EINVAL16); + did = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "device-id", PCI_EINVAL16); + if (vid == PCI_EINVAL16 || did == PCI_EINVAL16) { + dev_err(dip, CE_WARN, "failed to get PCI ID properties"); + return (DDI_FAILURE); + } + + if (vid != AMDZEN_PCI_VID_AMD) { + dev_err(dip, CE_WARN, "expected AMD vendor ID (0x%x), found " + "0x%x", AMDZEN_PCI_VID_AMD, vid); + return (DDI_FAILURE); + } + + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "reg", ®s, &nregs) != DDI_PROP_SUCCESS) { + dev_err(dip, CE_WARN, "failed to get 'reg' property"); + return (DDI_FAILURE); + } + + if (nregs == 0) { + ddi_prop_free(regs); + dev_err(dip, CE_WARN, "missing 'reg' property values"); + return (DDI_FAILURE); + } + reg = *regs; + ddi_prop_free(regs); + + for (i = 0; i < ARRAY_SIZE(amdzen_nb_ids); i++) { + if (amdzen_nb_ids[i] == did) { + valid = B_TRUE; + nb = B_TRUE; + } + } + + if (!valid && PCI_REG_BUS_G(reg) == AMDZEN_DF_BUSNO && + PCI_REG_DEV_G(reg) >= AMDZEN_DF_FIRST_DEVICE) { + valid = B_TRUE; + nb = B_FALSE; + } + + if (!valid) { + dev_err(dip, CE_WARN, "device %s didn't match the nexus list", + ddi_get_name(dip)); + return (DDI_FAILURE); + } + + stub = kmem_alloc(sizeof (amdzen_stub_t), KM_SLEEP); + if (pci_config_setup(dip, &stub->azns_cfgspace) != DDI_SUCCESS) { + dev_err(dip, CE_WARN, "failed to set up config space"); + kmem_free(stub, sizeof (amdzen_stub_t)); + return (DDI_FAILURE); + } + + stub->azns_dip = dip; + stub->azns_vid = vid; + stub->azns_did = did; + stub->azns_bus = PCI_REG_BUS_G(reg); + stub->azns_dev = PCI_REG_DEV_G(reg); + stub->azns_func = PCI_REG_FUNC_G(reg); + ddi_set_driver_private(dip, stub); + + mutex_enter(&azn->azn_mutex); + azn->azn_npresent++; + if (nb) { + list_insert_tail(&azn->azn_nb_stubs, stub); + } else { + list_insert_tail(&azn->azn_df_stubs, stub); + } + + if ((azn->azn_flags & AMDZEN_F_TASKQ_MASK) == AMDZEN_F_SCAN_COMPLETE && + azn->azn_nscanned == azn->azn_npresent) { + azn->azn_flags |= AMDZEN_F_ATTACH_DISPATCHED; + azn->azn_taskqid = taskq_dispatch(system_taskq, + amdzen_nexus_init, azn, TQ_SLEEP); + } + mutex_exit(&azn->azn_mutex); + + return (DDI_SUCCESS); +} + +static int +amdzen_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, + void *arg, void *result) +{ + char buf[32]; + dev_info_t *child; + const amdzen_child_data_t *acd; + + switch (ctlop) { + case DDI_CTLOPS_REPORTDEV: + if (rdip == NULL) { + return (DDI_FAILURE); + } + cmn_err(CE_CONT, "amdzen nexus: %s@%s, %s%d\n", + ddi_node_name(rdip), ddi_get_name_addr(rdip), + ddi_driver_name(rdip), ddi_get_instance(rdip)); + break; + case DDI_CTLOPS_INITCHILD: + child = arg; + if (child == NULL) { + dev_err(dip, CE_WARN, "!no child passed for " + "DDI_CTLOPS_INITCHILD"); + } + + acd = ddi_get_parent_data(child); + if (acd == NULL) { + dev_err(dip, CE_WARN, "!missing child parent data"); + return (DDI_FAILURE); + } + + if (snprintf(buf, sizeof (buf), "%d", acd->acd_addr) >= + sizeof (buf)) { + dev_err(dip, CE_WARN, "!failed to construct device " + "addr due to overflow"); + return (DDI_FAILURE); + } + + ddi_set_name_addr(child, buf); + break; + case DDI_CTLOPS_UNINITCHILD: + child = arg; + if (child == NULL) { + dev_err(dip, CE_WARN, "!no child passed for " + "DDI_CTLOPS_UNINITCHILD"); + } + + ddi_set_name_addr(child, NULL); + break; + default: + return (ddi_ctlops(dip, rdip, ctlop, arg, result)); + } + return (DDI_SUCCESS); +} + +static int +amdzen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + amdzen_t *azn = amdzen_data; + + if (cmd == DDI_RESUME) { + return (DDI_SUCCESS); + } else if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + mutex_enter(&azn->azn_mutex); + if (azn->azn_dip != NULL) { + dev_err(dip, CE_WARN, "driver is already attached!"); + mutex_exit(&azn->azn_mutex); + return (DDI_FAILURE); + } + + azn->azn_dip = dip; + azn->azn_taskqid = taskq_dispatch(system_taskq, amdzen_stub_scan, + azn, TQ_SLEEP); + azn->azn_flags |= AMDZEN_F_SCAN_DISPATCHED; + mutex_exit(&azn->azn_mutex); + + return (DDI_SUCCESS); +} + +static int +amdzen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + amdzen_t *azn = amdzen_data; + + if (cmd == DDI_SUSPEND) { + return (DDI_SUCCESS); + } else if (cmd != DDI_DETACH) { + return (DDI_FAILURE); + } + + mutex_enter(&azn->azn_mutex); + while (azn->azn_taskqid != TASKQID_INVALID) { + cv_wait(&azn->azn_cv, &azn->azn_mutex); + } + + /* + * If we've attached any stub drivers, e.g. this platform is important + * for us, then we fail detach. + */ + if (!list_is_empty(&azn->azn_df_stubs) || + !list_is_empty(&azn->azn_nb_stubs)) { + mutex_exit(&azn->azn_mutex); + return (DDI_FAILURE); + } + + azn->azn_dip = NULL; + mutex_exit(&azn->azn_mutex); + + return (DDI_SUCCESS); +} + +static void +amdzen_free(void) +{ + if (amdzen_data == NULL) { + return; + } + + VERIFY(list_is_empty(&amdzen_data->azn_df_stubs)); + list_destroy(&amdzen_data->azn_df_stubs); + VERIFY(list_is_empty(&amdzen_data->azn_nb_stubs)); + list_destroy(&amdzen_data->azn_nb_stubs); + cv_destroy(&amdzen_data->azn_cv); + mutex_destroy(&amdzen_data->azn_mutex); + kmem_free(amdzen_data, sizeof (amdzen_t)); + amdzen_data = NULL; +} + +static void +amdzen_alloc(void) +{ + amdzen_data = kmem_zalloc(sizeof (amdzen_t), KM_SLEEP); + mutex_init(&amdzen_data->azn_mutex, NULL, MUTEX_DRIVER, NULL); + list_create(&amdzen_data->azn_df_stubs, sizeof (amdzen_stub_t), + offsetof(amdzen_stub_t, azns_link)); + list_create(&amdzen_data->azn_nb_stubs, sizeof (amdzen_stub_t), + offsetof(amdzen_stub_t, azns_link)); + cv_init(&amdzen_data->azn_cv, NULL, CV_DRIVER, NULL); +} + +struct bus_ops amdzen_bus_ops = { + .busops_rev = BUSO_REV, + .bus_map = nullbusmap, + .bus_dma_map = ddi_no_dma_map, + .bus_dma_allochdl = ddi_no_dma_allochdl, + .bus_dma_freehdl = ddi_no_dma_freehdl, + .bus_dma_bindhdl = ddi_no_dma_bindhdl, + .bus_dma_unbindhdl = ddi_no_dma_unbindhdl, + .bus_dma_flush = ddi_no_dma_flush, + .bus_dma_win = ddi_no_dma_win, + .bus_dma_ctl = ddi_no_dma_mctl, + .bus_prop_op = ddi_bus_prop_op, + .bus_ctl = amdzen_bus_ctl +}; + +static struct dev_ops amdzen_dev_ops = { + .devo_rev = DEVO_REV, + .devo_refcnt = 0, + .devo_getinfo = nodev, + .devo_identify = nulldev, + .devo_probe = nulldev, + .devo_attach = amdzen_attach, + .devo_detach = amdzen_detach, + .devo_reset = nodev, + .devo_quiesce = ddi_quiesce_not_needed, + .devo_bus_ops = &amdzen_bus_ops +}; + +static struct modldrv amdzen_modldrv = { + .drv_modops = &mod_driverops, + .drv_linkinfo = "AMD Zen Nexus Driver", + .drv_dev_ops = &amdzen_dev_ops +}; + +static struct modlinkage amdzen_modlinkage = { + .ml_rev = MODREV_1, + .ml_linkage = { &amdzen_modldrv, NULL } +}; + +int +_init(void) +{ + int ret; + + if (cpuid_getvendor(CPU) != X86_VENDOR_AMD) { + return (ENOTSUP); + } + + if ((ret = mod_install(&amdzen_modlinkage)) == 0) { + amdzen_alloc(); + } + + return (ret); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&amdzen_modlinkage, modinfop)); +} + +int +_fini(void) +{ + int ret; + + if ((ret = mod_remove(&amdzen_modlinkage)) == 0) { + amdzen_free(); + } + + return (ret); +} diff --git a/usr/src/uts/intel/io/amdzen/amdzen.conf b/usr/src/uts/intel/io/amdzen/amdzen.conf new file mode 100644 index 0000000000..9c69d42c11 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/amdzen.conf @@ -0,0 +1,16 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +name="amdzen" parent="pseudo" instance=0; diff --git a/usr/src/uts/intel/io/amdzen/amdzen.h b/usr/src/uts/intel/io/amdzen/amdzen.h new file mode 100644 index 0000000000..8150495911 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/amdzen.h @@ -0,0 +1,306 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +#ifndef _AMDZEN_H +#define _AMDZEN_H + +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/list.h> +#include <sys/pci.h> +#include <sys/taskq.h> +#include <sys/bitmap.h> + +/* + * This header describes properties of the data fabric and our internal state + * for the Zen Nexus driver. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The data fabric devices are always defined to be on PCI bus zero starting at + * device 0x18. + */ +#define AMDZEN_DF_BUSNO 0x00 +#define AMDZEN_DF_FIRST_DEVICE 0x18 + +/* + * The maximum amount of Data Fabric node's we can see. In Zen 1 there were up + * to four per package. + */ +#define AMDZEN_MAX_DFS 0x8 + +/* + * The maximum number of PCI functions we expect to encounter on the data + * fabric. + */ +#define AMDZEN_MAX_DF_FUNCS 0x8 + + +/* + * Registers in the data fabric space that we care about for the purposes of the + * nexus driver understanding itself. + */ + +/* + * This set of registers provides us access to the count of instances in the + * data fabric and then a number of different pieces of information about them + * like their type. Note, these registers require indirect access because the + * information cannot be broadcast. + */ +#define AMDZEN_DF_F0_FBICNT 0x40 +#define AMDZEN_DF_F0_FBICNT_COUNT(x) BITX(x, 7, 0) +#define AMDZEN_DF_F0_FBIINFO0 0x44 +#define AMDZEN_DF_F0_FBIINFO0_TYPE(x) BITX(x, 3, 0) +typedef enum { + AMDZEN_DF_TYPE_CCM = 0, + AMDZEN_DF_TYPE_GCM, + AMDZEN_DF_TYPE_NCM, + AMDZEN_DF_TYPE_IOMS, + AMDZEN_DF_TYPE_CS, + AMDZEN_DF_TYPE_TCDX, + AMDZEN_DF_TYPE_PIE, + AMDZEN_DF_TYPE_SPF, + AMDZEN_DF_TYPE_LLC, + AMDZEN_DF_TYPE_CAKE +} amdzen_df_type_t; +#define AMDZEN_DF_F0_FBIINFO0_SDP_WIDTH(x) BITX(x, 5, 4) +typedef enum { + AMDZEN_DF_SDP_W_64 = 0, + AMDZEN_DF_SDP_W_128, + AMDZEN_DF_SDP_W_256, + AMDZEN_DF_SDP_W_512 +} amdzen_df_sdp_width_t; +#define AMDZEN_DF_F0_FBIINFO0_ENABLED(x) BITX(x, 6, 6) +#define AMDZEN_DF_F0_FBIINFO0_FTI_WIDTH(x) BITX(x, 9, 8) +typedef enum { + AMDZEN_DF_FTI_W_64 = 0, + AMDZEN_DF_FTI_W_128, + AMDZEN_DF_FTI_W_256, + AMDZEN_DF_FTI_W_512 +} amdzen_df_fti_width_t; +#define AMDZEN_DF_F0_FBIINFO0_SDP_PCOUNT(x) BITX(x, 13, 12) +#define AMDZEN_DF_F0_FBIINFO0_FTI_PCOUNT(x) BITX(x, 18, 16) +#define AMDZEN_DF_F0_FBIINFO0_HAS_MCA(x) BITX(x, 23, 23) +#define AMDZEN_DF_F0_FBIINFO0_SUBTYPE(x) BITX(x, 26, 24) +#define AMDZEN_DF_SUBTYPE_NONE 0 +typedef enum { + AMDZEN_DF_CAKE_SUBTYPE_GMI = 1, + AMDZEN_DF_CAKE_SUBTYPE_xGMI = 2 +} amdzen_df_cake_subtype_t; + +typedef enum { + AMDZEN_DF_IOM_SUBTYPE_IOHUB = 1, +} amdzen_df_iom_subtype_t; + +typedef enum { + AMDZEN_DF_CS_SUBTYPE_UMC = 1, + AMDZEN_DF_CS_SUBTYPE_CCIX = 2 +} amdzen_df_cs_subtype_t; + +#define AMDZEN_DF_F0_FBIINFO1 0x48 +#define AMDZEN_DF_F0_FBIINFO1_FTI0_NINSTID(x) BITX(x, 7, 0) +#define AMDZEN_DF_F0_FBIINFO1_FTI1_NINSTID(x) BITX(x, 15, 8) +#define AMDZEN_DF_F0_FBIINFO1_FTI2_NINSTID(x) BITX(x, 23, 16) +#define AMDZEN_DF_F0_FBIINFO1_FTI3_NINSTID(x) BITX(x, 31, 24) +#define AMDZEN_DF_F0_FBIINFO2 0x4c +#define AMDZEN_DF_F0_FBIINFO2_FTI4_NINSTID(x) BITX(x, 7, 0) +#define AMDZEN_DF_F0_FBIINFO2_FTI5_NINSTID(x) BITX(x, 15, 8) +#define AMDZEN_DF_F0_FBIINFO3 0x50 +#define AMDZEN_DF_F0_FBIINFO3_INSTID(x) BITX(x, 7, 0) +#define AMDZEN_DF_F0_FBIINFO3_FABID(x) BITX(x, 13, 8) + +/* + * This register contains the information about the configuration of PCIe buses. + * We care about finding which one has our BUS A, which is required to map it to + * the northbridge. + */ +#define AMDZEN_DF_F0_CFG_ADDR_CTL 0x84 +#define AMDZEN_DF_F0_CFG_ADDR_CTL_BUS_NUM(x) BITX(x, 7, 0) + +/* + * Registers that describe how the system is actually put together. + */ +#define AMDZEN_DF_F1_SYSCFG 0x200 +#define AMDZEN_DF_F1_SYSCFG_DIE_PRESENT(X) BITX(x, 7, 0) +#define AMDZEN_DF_F1_SYSCFG_DIE_TYPE(x) BITX(x, 18, 11) +#define AMDZEN_DF_F1_SYSCFG_MYDIE_TYPE(x) BITX(x, 24, 23) +typedef enum { + AMDZEN_DF_DIE_TYPE_CPU = 0, + AMDZEN_DF_DIE_TYPE_APU, + AMDZEN_DF_DIE_TYPE_dGPU +} amdzen_df_die_type_t; +#define AMDZEN_DF_F1_SYSCFG_OTHERDIE_TYPE(x) BITX(x, 26, 25) +#define AMDZEN_DF_F1_SYSCFG_OTHERSOCK(x) BITX(x, 27, 27) +#define AMDZEN_DF_F1_SYSCFG_NODEID(x) BITX(x, 30, 28) + +#define AMDZEN_DF_F1_FIDMASK0 0x208 +#define AMDZEN_DF_F1_FIDMASK0_COMP_MASK(x) BITX(x, 9, 0) +#define AMDZEN_DF_F1_FIDMASK0_NODE_MASK(x) BITX(x, 25, 16) +#define AMDZEN_DF_F1_FIDMASK1 0x20C +#define AMDZEN_DF_F1_FIDMASK1_NODE_SHIFT(x) BITX(x, 3, 0) +#define AMDZEN_DF_F1_FIDMASK1_SKT_SHIFT(x) BITX(x, 9, 8) +#define AMDZEN_DF_F1_FIDMASK1_DIE_MASK(x) BITX(x, 18, 16) +#define AMDZEN_DF_F1_FIDMASK1_SKT_MASK(x) BITX(x, 26, 24) + +/* + * These two registers define information about the PSP and SMU on local and + * remote dies (from the context of the DF instance). The bits are the same. + */ +#define AMDZEN_DF_F1_PSPSMU_LOCAL 0x268 +#define AMDZEN_DF_F1_PSPSMU_REMOTE 0x268 +#define AMDZEN_DF_F1_PSPSMU_SMU_VALID(x) BITX(x, 0, 0) +#define AMDZEN_DF_F1_PSPSMU_SMU_UNITID(x) BITX(x, 6, 1) +#define AMDZEN_DF_F1_PSPSMU_SMU_COMPID(x) BITX(x, 15, 8) +#define AMDZEN_DF_F1_PSPSMU_PSP_VALID(x) BITX(x, 16, 16) +#define AMDZEN_DF_F1_PSPSMU_PSP_UNITID(x) BITX(x, 22, 17) +#define AMDZEN_DF_F1_PSPSMU_PSP_COMPID(x) BITX(x, 31, 24) + +#define AMDZEN_DF_F1_CAKE_ENCR 0x2cc + +/* + * These registers are used to define Indirect Access, commonly known as FICAA + * and FICAD for the system. While there are multiple copies of the indirect + * access registers in device 4, we're only allowed access to one set of those + * (which are the ones present here). Specifically the OS is given access to set + * 3. + */ +#define AMDZEN_DF_F4_FICAA 0x5c +#define AMDZEN_DF_F4_FICAA_TARG_INST (1 << 0) +#define AMDZEN_DF_F4_FICAA_SET_REG(x) ((x) & 0x3fc) +#define AMDZEN_DF_F4_FICAA_SET_FUNC(x) (((x) & 0x7) << 11) +#define AMDZEN_DF_F4_FICAA_SET_64B (1 << 14) +#define AMDZEN_DF_F4_FICAA_SET_INST(x) (((x) & 0xff) << 16) +#define AMDZEN_DF_F4_FICAD_LO 0x98 +#define AMDZEN_DF_F4_FICAD_HI 0x9c + +/* + * Northbridge registers that are relevant for the nexus, mostly for SMN. + */ +#define AMDZEN_NB_SMN_ADDR 0x60 +#define AMDZEN_NB_SMN_DATA 0x64 + +/* + * AMD PCI ID for reference + */ +#define AMDZEN_PCI_VID_AMD 0x1022 + +typedef enum { + AMDZEN_STUB_TYPE_DF, + AMDZEN_STUB_TYPE_NB +} amdzen_stub_type_t; + +typedef struct { + list_node_t azns_link; + dev_info_t *azns_dip; + uint16_t azns_vid; + uint16_t azns_did; + uint16_t azns_bus; + uint16_t azns_dev; + uint16_t azns_func; + ddi_acc_handle_t azns_cfgspace; +} amdzen_stub_t; + +typedef enum { + AMDZEN_DFE_F_MCA = 1 << 0, + AMDZEN_DFE_F_ENABLED = 1 << 1 +} amdzen_df_ent_flags_t; + +typedef struct { + uint8_t adfe_drvid; + amdzen_df_ent_flags_t adfe_flags; + amdzen_df_type_t adfe_type; + uint8_t adfe_subtype; + uint8_t adfe_fabric_id; + uint8_t adfe_inst_id; + amdzen_df_sdp_width_t adfe_sdp_width; + amdzen_df_fti_width_t adfe_fti_width; + uint8_t adfe_sdp_count; + uint8_t adfe_fti_count; + uint32_t adfe_info0; + uint32_t adfe_info1; + uint32_t adfe_info2; + uint32_t adfe_info3; + uint32_t adfe_syscfg; + uint32_t adfe_mask0; + uint32_t adfe_mask1; +} amdzen_df_ent_t; + +typedef enum { + AMDZEN_DF_F_VALID = 1 << 0, + AMDZEN_DF_F_FOUND_NB = 1 << 1 +} amdzen_df_flags_t; + +typedef struct { + amdzen_df_flags_t adf_flags; + uint_t adf_nb_busno; + amdzen_stub_t *adf_funcs[AMDZEN_MAX_DF_FUNCS]; + amdzen_stub_t *adf_nb; + uint_t adf_nents; + amdzen_df_ent_t *adf_ents; + uint32_t adf_nodeid; + uint32_t adf_syscfg; + uint32_t adf_mask0; + uint32_t adf_mask1; +} amdzen_df_t; + +typedef enum { + AMDZEN_F_UNSUPPORTED = 1 << 0, + AMDZEN_F_DEVICE_ERROR = 1 << 1, + AMDZEN_F_MAP_ERROR = 1 << 2, + AMDZEN_F_SCAN_DISPATCHED = 1 << 3, + AMDZEN_F_SCAN_COMPLETE = 1 << 4, + AMDZEN_F_ATTACH_DISPATCHED = 1 << 5, + AMDZEN_F_ATTACH_COMPLETE = 1 << 6 +} amdzen_flags_t; + +#define AMDZEN_F_TASKQ_MASK (AMDZEN_F_SCAN_DISPATCHED | \ + AMDZEN_F_SCAN_COMPLETE | AMDZEN_F_ATTACH_DISPATCHED | \ + AMDZEN_F_ATTACH_COMPLETE) + +typedef struct amdzen { + kmutex_t azn_mutex; + kcondvar_t azn_cv; + amdzen_flags_t azn_flags; + dev_info_t *azn_dip; + taskqid_t azn_taskqid; + uint_t azn_nscanned; + uint_t azn_npresent; + list_t azn_df_stubs; + list_t azn_nb_stubs; + uint_t azn_ndfs; + amdzen_df_t azn_dfs[AMDZEN_MAX_DFS]; +} amdzen_t; + +typedef enum { + AMDZEN_C_SMNTEMP = 1, + AMDZEN_C_USMN, + AMDZEN_C_ZEN_UDF +} amdzen_child_t; + +/* + * Functions for stubs. + */ +extern int amdzen_attach_stub(dev_info_t *, ddi_attach_cmd_t); +extern int amdzen_detach_stub(dev_info_t *, ddi_detach_cmd_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _AMDZEN_H */ diff --git a/usr/src/uts/intel/io/amdzen/amdzen_client.h b/usr/src/uts/intel/io/amdzen/amdzen_client.h new file mode 100644 index 0000000000..41ca3e8afd --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/amdzen_client.h @@ -0,0 +1,38 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +#ifndef _AMDZEN_CLIENT_H +#define _AMDZEN_CLIENT_H + +/* + * This header provides client routines to clients of the amdzen nexus driver. + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int amdzen_c_smn_read32(uint_t, uint32_t, uint32_t *); +extern uint_t amdzen_c_df_count(void); +extern int amdzen_c_df_read32(uint_t, uint8_t, uint8_t, uint16_t, uint32_t *); +extern int amdzen_c_df_read64(uint_t, uint8_t, uint8_t, uint16_t, uint64_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _AMDZEN_CLIENT_H */ diff --git a/usr/src/uts/intel/io/amdzen/amdzen_stub.c b/usr/src/uts/intel/io/amdzen/amdzen_stub.c new file mode 100644 index 0000000000..f10849dc98 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/amdzen_stub.c @@ -0,0 +1,81 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +/* + * A stub driver for the AMD Zen Nexus. This is used to help us get all the + * relevant PCI devices into one place. See uts/intel/io/amdzen/amdzen.c for + * more details. + */ + +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> + +#include "amdzen.h" + +static int +amdzen_stub_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + return (amdzen_attach_stub(dip, cmd)); +} + +static int +amdzen_stub_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + return (amdzen_detach_stub(dip, cmd)); +} + +static struct dev_ops amdzen_stub_dev_ops = { + .devo_rev = DEVO_REV, + .devo_refcnt = 0, + .devo_getinfo = nodev, + .devo_identify = nodev, + .devo_probe = nulldev, + .devo_attach = amdzen_stub_attach, + .devo_detach = amdzen_stub_detach, + .devo_reset = nodev, + .devo_quiesce = ddi_quiesce_not_needed +}; + +static struct modldrv amdzen_stub_modldrv = { + .drv_modops = &mod_driverops, + .drv_linkinfo = "AMD Zen Nexus Stub driver", + .drv_dev_ops = &amdzen_stub_dev_ops +}; + +static struct modlinkage amdzen_stub_modlinkage = { + .ml_rev = MODREV_1, + .ml_linkage = { &amdzen_stub_modldrv, NULL } +}; + +int +_init(void) +{ + return (mod_install(&amdzen_stub_modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&amdzen_stub_modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&amdzen_stub_modlinkage)); +} diff --git a/usr/src/uts/intel/io/amdzen/smntemp.c b/usr/src/uts/intel/io/amdzen/smntemp.c new file mode 100644 index 0000000000..aa595f5ce5 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/smntemp.c @@ -0,0 +1,343 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019, Joyent, Inc. + * Copyright 2020 Oxide Computer Company + */ + +/* + * This implements a temperature sensor for AMD Zen family products that rely + * upon the SMN framework for getting temperature information. + */ + +#include <sys/modctl.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/types.h> +#include <sys/cred.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/cmn_err.h> +#include <sys/x86_archext.h> +#include <sys/cpuvar.h> +#include <sys/sensors.h> +#include <sys/sysmacros.h> +#include <amdzen_client.h> + +/* + * The following are register offsets and the meaning of their bits related to + * temperature. These addresses reside in the System Management Network which is + * accessed through the northbridge. They are not addresses in PCI configuration + * space. + */ +#define SMN_SMU_THERMAL_CURTEMP 0x00059800 +#define SMN_SMU_THERMAL_CURTEMP_TEMPERATURE(x) ((x) >> 21) +#define SMN_SMU_THERMAL_CURTEMP_RANGE_SEL (1 << 19) + +#define SMN_SMU_THERMAL_CURTEMP_RANGE_ADJ (-49) +#define SMN_SMU_THERMAL_CURTEMP_DECIMAL_BITS 3 +#define SMN_SMU_THERMAL_CURTEMP_BITS_MASK 0x7 + +/* + * The temperature sensor in Family 17 is measured in terms of 0.125 C steps. + */ +#define SMN_THERMAL_GRANULARITY 8 + +typedef enum { + SMNTEMP_F_MUTEX = 1 << 0 +} smntemp_flags_t; + +typedef struct { + uint_t stt_dfno; + id_t stt_ksensor; + struct smntemp *stt_smn; + smntemp_flags_t stt_flags; + kmutex_t stt_mutex; + hrtime_t stt_last_read; + uint32_t stt_reg; + int64_t stt_temp; +} smntemp_temp_t; + +typedef struct smntemp { + dev_info_t *smn_dip; + uint_t smn_ntemps; + int smn_offset; + smntemp_temp_t *smn_temps; +} smntemp_t; + +static smntemp_t smntemp_data; + +/* + * AMD processors report a control temperature (called Tctl) which may be + * different from the junction temperature, which is the value that is actually + * measured from the die (sometimes called Tdie or Tjct). This is done so that + * socket-based environmental monitoring can be consistent from a platform + * perspective, but doesn't help us. Unfortunately, these values aren't in + * datasheets that we can find, but have been documented partially in a series + * of blog posts by AMD when discussing their 'Ryzen Master' monitoring software + * for Windows. + * + * The brand strings below may contain partial matches such in the Threadripper + * cases so we can match the entire family of processors. The offset value is + * the quantity in degrees that we should adjust Tctl to reach Tdie. + */ +typedef struct { + const char *sto_brand; + uint_t sto_family; + int sto_off; +} smntemp_offset_t; + +static const smntemp_offset_t smntemp_offsets[] = { + { "AMD Ryzen 5 1600X", 0x17, -20 }, + { "AMD Ryzen 7 1700X", 0x17, -20 }, + { "AMD Ryzen 7 1800X", 0x17, -20 }, + { "AMD Ryzen 7 2700X", 0x17, -10 }, + { "AMD Ryzen Threadripper 19", 0x17, -27 }, + { "AMD Ryzen Threadripper 29", 0x17, -27 }, + { NULL } +}; + +static int +smntemp_temp_update(smntemp_t *smn, smntemp_temp_t *stt) +{ + int ret; + uint32_t reg; + int64_t raw, decimal; + + ASSERT(MUTEX_HELD((&stt->stt_mutex))); + + if ((ret = amdzen_c_smn_read32(stt->stt_dfno, SMN_SMU_THERMAL_CURTEMP, + ®)) != 0) { + return (ret); + } + + stt->stt_last_read = gethrtime(); + stt->stt_reg = reg; + raw = SMN_SMU_THERMAL_CURTEMP_TEMPERATURE(reg) >> + SMN_SMU_THERMAL_CURTEMP_DECIMAL_BITS; + decimal = SMN_SMU_THERMAL_CURTEMP_TEMPERATURE(reg) & + SMN_SMU_THERMAL_CURTEMP_BITS_MASK; + if ((reg & SMN_SMU_THERMAL_CURTEMP_RANGE_SEL) != 0) { + raw += SMN_SMU_THERMAL_CURTEMP_RANGE_ADJ; + } + raw += smn->smn_offset; + stt->stt_temp = raw << SMN_SMU_THERMAL_CURTEMP_DECIMAL_BITS; + stt->stt_temp += decimal; + + return (0); +} + +static int +smntemp_temp_read(void *arg, sensor_ioctl_scalar_t *temp) +{ + int ret; + smntemp_temp_t *stt = arg; + smntemp_t *smn = stt->stt_smn; + + mutex_enter(&stt->stt_mutex); + if ((ret = smntemp_temp_update(smn, stt)) != 0) { + mutex_exit(&stt->stt_mutex); + return (ret); + } + + temp->sis_unit = SENSOR_UNIT_CELSIUS; + temp->sis_value = stt->stt_temp; + temp->sis_gran = SMN_THERMAL_GRANULARITY; + mutex_exit(&stt->stt_mutex); + + return (0); +} + +static const ksensor_ops_t smntemp_temp_ops = { + .kso_kind = ksensor_kind_temperature, + .kso_scalar = smntemp_temp_read +}; + +static void +smntemp_cleanup(smntemp_t *smn) +{ + if (smn->smn_temps != NULL) { + uint_t i; + + (void) ksensor_remove(smn->smn_dip, KSENSOR_ALL_IDS); + for (i = 0; i < smn->smn_ntemps; i++) { + if ((smn->smn_temps[i].stt_flags & SMNTEMP_F_MUTEX) != + 0) { + mutex_destroy(&smn->smn_temps[i].stt_mutex); + smn->smn_temps[i].stt_flags &= ~SMNTEMP_F_MUTEX; + } + } + kmem_free(smn->smn_temps, sizeof (smntemp_temp_t) * + smn->smn_ntemps); + smn->smn_temps = NULL; + smn->smn_ntemps = 0; + } + + if (smn->smn_dip != NULL) { + ddi_remove_minor_node(smn->smn_dip, NULL); + ddi_set_driver_private(smn->smn_dip, NULL); + smn->smn_dip = NULL; + } +} + +static boolean_t +smntemp_find_offset(smntemp_t *smn) +{ + uint_t i, family; + char buf[256]; + + if (cpuid_getbrandstr(CPU, buf, sizeof (buf)) >= sizeof (buf)) { + dev_err(smn->smn_dip, CE_WARN, "!failed to read processor " + "brand string, brand larger than internal buffer"); + return (B_FALSE); + } + + family = cpuid_getfamily(CPU); + + for (i = 0; i < ARRAY_SIZE(smntemp_offsets); i++) { + if (family != smntemp_offsets[i].sto_family) + continue; + if (strncmp(buf, smntemp_offsets[i].sto_brand, + strlen(smntemp_offsets[i].sto_brand)) == 0) { + smn->smn_offset = smntemp_offsets[i].sto_off; + break; + } + } + + return (B_TRUE); +} + +static int +smntemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + uint_t i; + smntemp_t *smntemp = &smntemp_data; + + if (cmd == DDI_RESUME) { + return (DDI_SUCCESS); + } else if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + if (smntemp->smn_dip != NULL) { + dev_err(dip, CE_WARN, "!smntemp already attached"); + return (DDI_FAILURE); + } + smntemp->smn_dip = dip; + ddi_set_driver_private(dip, smntemp); + + if (!smntemp_find_offset(smntemp)) { + goto err; + } + + smntemp->smn_ntemps = amdzen_c_df_count(); + if (smntemp->smn_ntemps == 0) { + dev_err(dip, CE_WARN, "!found zero DFs, can't attach smntemp"); + goto err; + } + smntemp->smn_temps = kmem_zalloc(sizeof (smntemp_temp_t) * + smntemp->smn_ntemps, KM_SLEEP); + for (i = 0; i < smntemp->smn_ntemps; i++) { + int ret; + char buf[128]; + + smntemp->smn_temps[i].stt_smn = smntemp; + smntemp->smn_temps[i].stt_dfno = i; + mutex_init(&smntemp->smn_temps[i].stt_mutex, NULL, MUTEX_DRIVER, + NULL); + smntemp->smn_temps[i].stt_flags |= SMNTEMP_F_MUTEX; + + if (snprintf(buf, sizeof (buf), "procnode.%u", i) >= + sizeof (buf)) { + dev_err(dip, CE_WARN, "!unexpected buffer name overrun " + "assembling temperature minor %u", i); + goto err; + } + + if ((ret = ksensor_create(dip, &smntemp_temp_ops, + &smntemp->smn_temps[i], buf, DDI_NT_SENSOR_TEMP_CPU, + &smntemp->smn_temps[i].stt_ksensor)) != 0) { + dev_err(dip, CE_WARN, "!failed to create sensor %s: %d", + buf, ret); + goto err; + } + } + + return (DDI_SUCCESS); + +err: + smntemp_cleanup(smntemp); + return (DDI_FAILURE); +} + +static int +smntemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + smntemp_t *smntemp = &smntemp_data; + + if (cmd == DDI_SUSPEND) { + return (DDI_SUCCESS); + } else if (cmd != DDI_DETACH) { + return (DDI_FAILURE); + } + + if (smntemp->smn_dip == NULL) { + dev_err(smntemp->smn_dip, CE_WARN, "!asked to detach smn " + "instance %d that was never attached", + ddi_get_instance(dip)); + return (DDI_FAILURE); + } + + smntemp_cleanup(smntemp); + return (DDI_SUCCESS); +} + +static struct dev_ops smntemp_dev_ops = { + .devo_rev = DEVO_REV, + .devo_refcnt = 0, + .devo_getinfo = nodev, + .devo_identify = nulldev, + .devo_probe = nulldev, + .devo_attach = smntemp_attach, + .devo_detach = smntemp_detach, + .devo_reset = nodev, + .devo_quiesce = ddi_quiesce_not_needed, +}; + +static struct modldrv smntemp_modldrv = { + .drv_modops = &mod_driverops, + .drv_linkinfo = "AMD SMN Temperature Driver", + .drv_dev_ops = &smntemp_dev_ops +}; + +static struct modlinkage smntemp_modlinkage = { + .ml_rev = MODREV_1, + .ml_linkage = { &smntemp_modldrv, NULL } +}; + +int +_init(void) +{ + return (mod_install(&smntemp_modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&smntemp_modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&smntemp_modlinkage)); +} diff --git a/usr/src/uts/intel/io/amdzen/usmn.c b/usr/src/uts/intel/io/amdzen/usmn.c new file mode 100644 index 0000000000..a219f997e1 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/usmn.c @@ -0,0 +1,275 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +/* + * A device driver that provides user access to the AMD System Management + * Network for debugging purposes. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/open.h> +#include <sys/cred.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/cmn_err.h> +#include <sys/policy.h> +#include <amdzen_client.h> + +#include "usmn.h" + +typedef struct usmn { + dev_info_t *usmn_dip; + uint_t usmn_ndfs; +} usmn_t; + +static usmn_t usmn_data; + +static int +usmn_open(dev_t *devp, int flags, int otype, cred_t *credp) +{ + minor_t m; + usmn_t *usmn = &usmn_data; + + if (crgetzoneid(credp) != GLOBAL_ZONEID || + secpolicy_hwmanip(credp) != 0) { + return (EPERM); + } + + if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) { + return (EINVAL); + } + + if (otype != OTYP_CHR) { + return (EINVAL); + } + + m = getminor(*devp); + if (m >= usmn->usmn_ndfs) { + return (ENXIO); + } + + return (0); +} + +static int +usmn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + uint_t dfno; + usmn_t *usmn = &usmn_data; + usmn_reg_t usr; + + if (cmd != USMN_READ && cmd != USMN_WRITE) { + return (ENOTTY); + } + + dfno = getminor(dev); + if (dfno >= usmn->usmn_ndfs) { + return (ENXIO); + } + + if (crgetzoneid(credp) != GLOBAL_ZONEID || + secpolicy_hwmanip(credp) != 0) { + return (EPERM); + } + + if (ddi_copyin((void *)arg, &usr, sizeof (usr), mode & FKIOCTL) != 0) { + return (EFAULT); + } + + if (cmd == USMN_READ) { + int ret; + + ret = amdzen_c_smn_read32(dfno, usr.usr_addr, &usr.usr_data); + if (ret != 0) { + return (ret); + } + } else { + return (ENOTSUP); + } + + if (ddi_copyout(&usr, (void *)arg, sizeof (usr), mode & FKIOCTL) != 0) { + return (EFAULT); + } + + return (0); +} + +static int +usmn_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + return (0); +} + +static void +usmn_cleanup(usmn_t *usmn) +{ + ddi_remove_minor_node(usmn->usmn_dip, NULL); + usmn->usmn_ndfs = 0; + usmn->usmn_dip = NULL; +} + +static int +usmn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + usmn_t *usmn = &usmn_data; + + if (cmd == DDI_RESUME) { + return (DDI_SUCCESS); + } else if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + if (usmn->usmn_dip != NULL) { + dev_err(dip, CE_WARN, "!usmn is already attached to a " + "dev_info_t: %p", usmn->usmn_dip); + return (DDI_FAILURE); + } + + usmn->usmn_dip = dip; + usmn->usmn_ndfs = amdzen_c_df_count(); + for (uint_t i = 0; i < usmn->usmn_ndfs; i++) { + char buf[32]; + + (void) snprintf(buf, sizeof (buf), "usmn.%u", i); + if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO, + 0) != DDI_SUCCESS) { + dev_err(dip, CE_WARN, "!failed to create minor %s", + buf); + goto err; + } + } + + return (DDI_SUCCESS); + +err: + usmn_cleanup(usmn); + return (DDI_FAILURE); +} + +static int +usmn_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) +{ + usmn_t *usmn = &usmn_data; + minor_t m; + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + m = getminor((dev_t)arg); + if (m >= usmn->usmn_ndfs) { + return (DDI_FAILURE); + } + *resultp = (void *)usmn->usmn_dip; + break; + case DDI_INFO_DEVT2INSTANCE: + m = getminor((dev_t)arg); + if (m >= usmn->usmn_ndfs) { + return (DDI_FAILURE); + } + *resultp = (void *)(uintptr_t)ddi_get_instance(usmn->usmn_dip); + break; + default: + return (DDI_FAILURE); + } + return (DDI_SUCCESS); +} + +static int +usmn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + usmn_t *usmn = &usmn_data; + + if (cmd == DDI_SUSPEND) { + return (DDI_SUCCESS); + } else if (cmd != DDI_DETACH) { + return (DDI_FAILURE); + } + + if (usmn->usmn_dip != dip) { + dev_err(dip, CE_WARN, "!asked to detach usmn, but dip doesn't " + "match"); + return (DDI_FAILURE); + } + + usmn_cleanup(usmn); + return (DDI_SUCCESS); +} + +static struct cb_ops usmn_cb_ops = { + .cb_open = usmn_open, + .cb_close = usmn_close, + .cb_strategy = nodev, + .cb_print = nodev, + .cb_dump = nodev, + .cb_read = nodev, + .cb_write = nodev, + .cb_ioctl = usmn_ioctl, + .cb_devmap = nodev, + .cb_mmap = nodev, + .cb_segmap = nodev, + .cb_chpoll = nochpoll, + .cb_prop_op = ddi_prop_op, + .cb_flag = D_MP, + .cb_rev = CB_REV, + .cb_aread = nodev, + .cb_awrite = nodev +}; + +static struct dev_ops usmn_dev_ops = { + .devo_rev = DEVO_REV, + .devo_refcnt = 0, + .devo_getinfo = usmn_getinfo, + .devo_identify = nulldev, + .devo_probe = nulldev, + .devo_attach = usmn_attach, + .devo_detach = usmn_detach, + .devo_reset = nodev, + .devo_quiesce = ddi_quiesce_not_needed, + .devo_cb_ops = &usmn_cb_ops +}; + +static struct modldrv usmn_modldrv = { + .drv_modops = &mod_driverops, + .drv_linkinfo = "AMD User SMN Access", + .drv_dev_ops = &usmn_dev_ops +}; + +static struct modlinkage usmn_modlinkage = { + .ml_rev = MODREV_1, + .ml_linkage = { &usmn_modldrv, NULL } +}; + +int +_init(void) +{ + return (mod_install(&usmn_modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&usmn_modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&usmn_modlinkage)); +} diff --git a/usr/src/uts/intel/io/amdzen/usmn.h b/usr/src/uts/intel/io/amdzen/usmn.h new file mode 100644 index 0000000000..10f057525d --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/usmn.h @@ -0,0 +1,41 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +#ifndef _USMN_H +#define _USMN_H + +/* + * Private ioctls for interfacing with the usmn driver. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define USMN_IOCTL (('u' << 24) | ('s' << 16) | ('m' << 8)) + +#define USMN_READ (USMN_IOCTL | 0x01) +#define USMN_WRITE (USMN_IOCTL | 0x02) + +typedef struct usmn_reg { + uint32_t usr_addr; + uint32_t usr_data; +} usmn_reg_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _USMN_H */ diff --git a/usr/src/uts/intel/io/amdzen/zen_udf.c b/usr/src/uts/intel/io/amdzen/zen_udf.c new file mode 100644 index 0000000000..61d1e79774 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/zen_udf.c @@ -0,0 +1,286 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +/* + * A companion to zen_udf(7D) that allows user access to read the data fabric + * for development purposes. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/open.h> +#include <sys/cred.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/devops.h> +#include <sys/cmn_err.h> +#include <sys/policy.h> +#include <amdzen_client.h> + +#include <zen_udf.h> + +typedef struct zen_udf { + dev_info_t *zudf_dip; + uint_t zudf_ndfs; +} zen_udf_t; + +static zen_udf_t zen_udf_data; + +static int +zen_udf_open(dev_t *devp, int flags, int otype, cred_t *credp) +{ + minor_t m; + zen_udf_t *zen_udf = &zen_udf_data; + + if (crgetzoneid(credp) != GLOBAL_ZONEID || + secpolicy_hwmanip(credp) != 0) { + return (EPERM); + } + + if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) { + return (EINVAL); + } + + if (otype != OTYP_CHR) { + return (EINVAL); + } + + m = getminor(*devp); + if (m >= zen_udf->zudf_ndfs) { + return (ENXIO); + } + + return (0); +} + +static int +zen_udf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + uint_t dfno; + zen_udf_t *zen_udf = &zen_udf_data; + zen_udf_io_t zui; + + if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) { + return (ENOTTY); + } + + dfno = getminor(dev); + if (dfno >= zen_udf->zudf_ndfs) { + return (ENXIO); + } + + if (crgetzoneid(credp) != GLOBAL_ZONEID || + secpolicy_hwmanip(credp) != 0) { + return (EPERM); + } + + if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) { + return (EFAULT); + } + + if (cmd == ZEN_UDF_READ32) { + int ret; + uint32_t data; + + ret = amdzen_c_df_read32(dfno, zui.zui_inst, zui.zui_func, + zui.zui_reg, &data); + if (ret != 0) { + return (ret); + } + + zui.zui_data = data; + } else { + int ret; + + ret = amdzen_c_df_read64(dfno, zui.zui_inst, zui.zui_func, + zui.zui_reg, &zui.zui_data); + if (ret != 0) { + return (ret); + } + } + + if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) { + return (EFAULT); + } + + return (0); +} + +static int +zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + return (0); +} + +static void +zen_udf_cleanup(zen_udf_t *zen_udf) +{ + ddi_remove_minor_node(zen_udf->zudf_dip, NULL); + zen_udf->zudf_ndfs = 0; + zen_udf->zudf_dip = NULL; +} + +static int +zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + zen_udf_t *zen_udf = &zen_udf_data; + + if (cmd == DDI_RESUME) { + return (DDI_SUCCESS); + } else if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + if (zen_udf->zudf_dip != NULL) { + dev_err(dip, CE_WARN, "!zen_udf is already attached to a " + "dev_info_t: %p", zen_udf->zudf_dip); + return (DDI_FAILURE); + } + + zen_udf->zudf_dip = dip; + zen_udf->zudf_ndfs = amdzen_c_df_count(); + for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) { + char buf[32]; + + (void) snprintf(buf, sizeof (buf), "zen_udf.%u", i); + if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO, + 0) != DDI_SUCCESS) { + dev_err(dip, CE_WARN, "!failed to create minor %s", + buf); + goto err; + } + } + + return (DDI_SUCCESS); + +err: + zen_udf_cleanup(zen_udf); + return (DDI_FAILURE); +} + +static int +zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) +{ + zen_udf_t *zen_udf = &zen_udf_data; + minor_t m; + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + m = getminor((dev_t)arg); + if (m >= zen_udf->zudf_ndfs) { + return (DDI_FAILURE); + } + *resultp = (void *)zen_udf->zudf_dip; + break; + case DDI_INFO_DEVT2INSTANCE: + m = getminor((dev_t)arg); + if (m >= zen_udf->zudf_ndfs) { + return (DDI_FAILURE); + } + *resultp = (void *)(uintptr_t)ddi_get_instance( + zen_udf->zudf_dip); + break; + default: + return (DDI_FAILURE); + } + return (DDI_SUCCESS); +} + +static int +zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + zen_udf_t *zen_udf = &zen_udf_data; + + if (cmd == DDI_SUSPEND) { + return (DDI_SUCCESS); + } else if (cmd != DDI_DETACH) { + return (DDI_FAILURE); + } + + if (zen_udf->zudf_dip != dip) { + dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip " + "doesn't match"); + return (DDI_FAILURE); + } + + zen_udf_cleanup(zen_udf); + return (DDI_SUCCESS); +} + +static struct cb_ops zen_udf_cb_ops = { + .cb_open = zen_udf_open, + .cb_close = zen_udf_close, + .cb_strategy = nodev, + .cb_print = nodev, + .cb_dump = nodev, + .cb_read = nodev, + .cb_write = nodev, + .cb_ioctl = zen_udf_ioctl, + .cb_devmap = nodev, + .cb_mmap = nodev, + .cb_segmap = nodev, + .cb_chpoll = nochpoll, + .cb_prop_op = ddi_prop_op, + .cb_flag = D_MP, + .cb_rev = CB_REV, + .cb_aread = nodev, + .cb_awrite = nodev +}; + +static struct dev_ops zen_udf_dev_ops = { + .devo_rev = DEVO_REV, + .devo_refcnt = 0, + .devo_getinfo = zen_udf_getinfo, + .devo_identify = nulldev, + .devo_probe = nulldev, + .devo_attach = zen_udf_attach, + .devo_detach = zen_udf_detach, + .devo_reset = nodev, + .devo_quiesce = ddi_quiesce_not_needed, + .devo_cb_ops = &zen_udf_cb_ops +}; + +static struct modldrv zen_udf_modldrv = { + .drv_modops = &mod_driverops, + .drv_linkinfo = "AMD User DF Access", + .drv_dev_ops = &zen_udf_dev_ops +}; + +static struct modlinkage zen_udf_modlinkage = { + .ml_rev = MODREV_1, + .ml_linkage = { &zen_udf_modldrv, NULL } +}; + +int +_init(void) +{ + return (mod_install(&zen_udf_modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&zen_udf_modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&zen_udf_modlinkage)); +} diff --git a/usr/src/uts/intel/io/amdzen/zen_udf.h b/usr/src/uts/intel/io/amdzen/zen_udf.h new file mode 100644 index 0000000000..ef5a3184d5 --- /dev/null +++ b/usr/src/uts/intel/io/amdzen/zen_udf.h @@ -0,0 +1,44 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Oxide Computer Company + */ + +#ifndef _ZEN_UDF_H +#define _ZEN_UDF_H + +/* + * Private ioctls for interfacing with the zen_udf driver. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZEN_UDF_IOCTL (('u' << 24) | ('d' << 16) | ('f' << 8)) + +#define ZEN_UDF_READ32 (ZEN_UDF_IOCTL | 0x01) +#define ZEN_UDF_READ64 (ZEN_UDF_IOCTL | 0x02) + +typedef struct zen_udf_io { + uint8_t zui_inst; + uint8_t zui_func; + uint16_t zui_reg; + uint32_t zui_pad; + uint64_t zui_data; +} zen_udf_io_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEN_UDF_H */ diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases index a55b9802c1..6c79bd182c 100644 --- a/usr/src/uts/intel/os/driver_aliases +++ b/usr/src/uts/intel/os/driver_aliases @@ -67,13 +67,50 @@ amd64_gart "pci1022,1103" amd8111s "pci1022,7462" amd_iommu "pci1002,5a23" amd_iommu "pci1022,11ff" -amdf17nbdf "pci1022,1440,p" -amdf17nbdf "pci1022,1450,p" -amdf17nbdf "pci1022,1460,p" -amdf17nbdf "pci1022,1480,p" -amdf17nbdf "pci1022,1490,p" -amdf17nbdf "pci1022,15d0,p" -amdf17nbdf "pci1022,15e8,p" +amdzen_stub "pci1022,1440,p" +amdzen_stub "pci1022,1441,p" +amdzen_stub "pci1022,1442,p" +amdzen_stub "pci1022,1443,p" +amdzen_stub "pci1022,1444,p" +amdzen_stub "pci1022,1445,p" +amdzen_stub "pci1022,1446,p" +amdzen_stub "pci1022,1447,p" +amdzen_stub "pci1022,1448,p" +amdzen_stub "pci1022,1449,p" +amdzen_stub "pci1022,144a,p" +amdzen_stub "pci1022,144b,p" +amdzen_stub "pci1022,144c,p" +amdzen_stub "pci1022,144d,p" +amdzen_stub "pci1022,144e,p" +amdzen_stub "pci1022,144f,p" +amdzen_stub "pci1022,1450,p" +amdzen_stub "pci1022,1460,p" +amdzen_stub "pci1022,1461,p" +amdzen_stub "pci1022,1462,p" +amdzen_stub "pci1022,1463,p" +amdzen_stub "pci1022,1464,p" +amdzen_stub "pci1022,1465,p" +amdzen_stub "pci1022,1466,p" +amdzen_stub "pci1022,1467,p" +amdzen_stub "pci1022,1480,p" +amdzen_stub "pci1022,1490,p" +amdzen_stub "pci1022,1491,p" +amdzen_stub "pci1022,1492,p" +amdzen_stub "pci1022,1493,p" +amdzen_stub "pci1022,1494,p" +amdzen_stub "pci1022,1495,p" +amdzen_stub "pci1022,1496,p" +amdzen_stub "pci1022,1497,p" +amdzen_stub "pci1022,15d0,p" +amdzen_stub "pci1022,15e8,p" +amdzen_stub "pci1022,15e9,p" +amdzen_stub "pci1022,15ea,p" +amdzen_stub "pci1022,15eb,p" +amdzen_stub "pci1022,15ec,p" +amdzen_stub "pci1022,15ed,p" +amdzen_stub "pci1022,15ee,p" +amdzen_stub "pci1022,15ef,p" +amdzen_stub "pci1022,1630,p" amdnbtemp "pci1022,1203,p" amdnbtemp "pci1022,1303,p" amdnbtemp "pci1022,1403,p" @@ -1365,6 +1402,7 @@ sfxge "pci1924,903" sfxge "pci1924,923" sgen "scsa,08.bfcp" sgen "scsa,08.bvhci" +smntemp "smntemp" smrt "pci103c,1920" smrt "pci103c,1921" smrt "pci103c,1922" diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index d4c9c1ebe6..d557fd918f 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -311,3 +311,6 @@ imc 314 ccid 315 ksensor 316 mlxcx 317 +amdzen_stub 318 +amdzen 319 +smntemp 320 diff --git a/usr/src/uts/intel/smntemp/Makefile b/usr/src/uts/intel/smntemp/Makefile new file mode 100644 index 0000000000..119944e10d --- /dev/null +++ b/usr/src/uts/intel/smntemp/Makefile @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer company +# + +UTSBASE = ../.. + +MODULE = smntemp +OBJECTS = $(SMNTEMP_OBJS:%=$(OBJS_DIR)/%) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +include $(UTSBASE)/intel/Makefile.intel + +ALL_TARGET = $(BINARY) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +CPPFLAGS += -I$(UTSBASE)/intel/io/amdzen +LDFLAGS += -dy -Ndrv/amdzen + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +install: $(INSTALL_DEPS) + +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/usmn/Makefile b/usr/src/uts/intel/usmn/Makefile new file mode 100644 index 0000000000..50e825d560 --- /dev/null +++ b/usr/src/uts/intel/usmn/Makefile @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +UTSBASE = ../.. + +MODULE = usmn +OBJECTS = $(USMN_OBJS:%=$(OBJS_DIR)/%) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +include $(UTSBASE)/intel/Makefile.intel + +ALL_TARGET = $(BINARY) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +CPPFLAGS += -I$(UTSBASE)/intel/io/amdzen +LDFLAGS += -dy -Ndrv/amdzen + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +install: $(INSTALL_DEPS) + +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/zen_udf/Makefile b/usr/src/uts/intel/zen_udf/Makefile new file mode 100644 index 0000000000..5b312d7013 --- /dev/null +++ b/usr/src/uts/intel/zen_udf/Makefile @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Oxide Computer Company +# + +UTSBASE = ../.. + +MODULE = zen_udf +OBJECTS = $(ZEN_UDF_OBJS:%=$(OBJS_DIR)/%) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +include $(UTSBASE)/intel/Makefile.intel + +ALL_TARGET = $(BINARY) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) +CPPFLAGS += -I$(UTSBASE)/intel/io/amdzen +LDFLAGS += -dy -Ndrv/amdzen + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +install: $(INSTALL_DEPS) + +include $(UTSBASE)/intel/Makefile.targ |