diff options
author | schwartz <none@none> | 2007-06-06 13:05:49 -0700 |
---|---|---|
committer | schwartz <none@none> | 2007-06-06 13:05:49 -0700 |
commit | 2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9 (patch) | |
tree | 3f14bba948390bf71a80d57812d56948c29c8bd6 /usr/src | |
parent | c4e3866948e749beb3671a51c589da2ba299dc03 (diff) | |
download | illumos-gate-2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9.tar.gz |
PSARC/2007/301 PCItool extensions for handling groups of interrupt vectors
PSARC/2007/302 PSM_INTR_OPS extensions for handling groups of interrupt vectors
6458838 Once intrd performs reassignment, MSI interrupts stop coming
6564773 Cleanup pcitool versioning
6565502 apic_rebind could write IOAPIC for fixed interrupts
Diffstat (limited to 'usr/src')
22 files changed, 931 insertions, 382 deletions
diff --git a/usr/src/cmd/intrd/intrd.pl b/usr/src/cmd/intrd/intrd.pl index 0c47a647bd..89692891e5 100755 --- a/usr/src/cmd/intrd/intrd.pl +++ b/usr/src/cmd/intrd/intrd.pl @@ -21,7 +21,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -68,7 +68,7 @@ while ($_ = shift @ARGV) { if ($using_scengen == 0) { require Sun::Solaris::Kstat; require Sun::Solaris::Intrs; - import Sun::Solaris::Intrs(qw(intrmove)); + import Sun::Solaris::Intrs(qw(intrmove is_pcplusmp)); require Sys::Syslog; import Sys::Syslog; openlog($cmdname, 'pid', 'daemon'); @@ -76,7 +76,6 @@ if ($using_scengen == 0) { &Sys::Syslog::LOG_INFO)); } - my $asserted = 0; my $assert_level = 'debug'; # syslog level for assertion failures sub VERIFY($@) @@ -93,7 +92,7 @@ sub VERIFY($@) -sub getstat($); +sub getstat($$); sub generate_delta($$); sub compress_deltas($); sub dumpdelta($); @@ -142,19 +141,25 @@ sub do_reconfig_cpu($$$); # private function # ->{"pil"} == pci_intrs:<ivec#>:<nexus>:pil # ->{"crtime"} == pci_intrs:<ivec#>:<nexus>:crtime # ->{"ino"} == pci_intrs:<ivec#>:<nexus>:ino +# ->{"num_ino"} == num inos of single device instance sharing this entry +# Will be > 1 on pcplusmp X86 systems for devices +# with multiple MSI interrupts. # ->{"buspath"} == pci_intrs:<ivec#>:<nexus>:buspath # ->{"name"} == pci_intrs:<ivec#>:<nexus>:name # ->{"ihs"} == pci_intrs:<ivec#>:<nexus>:ihs # -sub getstat($) +sub getstat($$) { - my ($ks) = @_; + my ($ks, $pcplusmp_sys) = @_; my $cpucnt = 0; my %stat = (); my ($minsnap, $maxsnap); + # Hash of hash which matches (MSI device, ino) combos to kstats. + my %msidevs = (); + # kstats are not generated atomically. Each kstat hierarchy will # have been generated within the kernel at a different time. On a # thrashing system, we may not run quickly enough in order to get @@ -177,6 +182,8 @@ sub getstat($) while (my ($cpu, $cpst) = each %{$ks->{cpu}}) { next if !exists($ks->{cpu_info}{$cpu}{"cpu_info$cpu"}{state}); + #"state" fld of kstat w/ + # modname inst name-"cpuinfo0" my $state = $ks->{cpu_info}{$cpu}{"cpu_info$cpu"}{state}; next if ($state !~ /^on-line\0/); my $cpu_sys = $cpst->{sys}; @@ -214,6 +221,10 @@ sub getstat($) next unless exists $stat{$cpu}; next if ($intrcfg->{type} =~ /^disabled\0/); + # Perl looks beyond NULL chars in pattern matching. + # Truncate name field at the first NULL + $intrcfg->{name} =~ s/\0.*$//; + if ($intrcfg->{snaptime} < $minsnap) { $minsnap = $intrcfg->{snaptime}; } elsif ($intrcfg->{snaptime} > $maxsnap) { @@ -242,9 +253,62 @@ sub getstat($) $stat{$cpu}{ivecs}{$cookie}{crtime} = $intrcfg->{crtime}; $stat{$cpu}{ivecs}{$cookie}{pil} = $intrcfg->{pil}; $stat{$cpu}{ivecs}{$cookie}{ino} = $intrcfg->{ino}; + $stat{$cpu}{ivecs}{$cookie}{num_ino} = 1; $stat{$cpu}{ivecs}{$cookie}{buspath} = $intrcfg->{buspath}; $stat{$cpu}{ivecs}{$cookie}{name} = $intrcfg->{name}; $stat{$cpu}{ivecs}{$cookie}{ihs} = 1; + + if ($pcplusmp_sys && ($intrcfg->{type} =~ /^msi\0/)) { + if (!(exists($msidevs{$intrcfg->{name}}))) { + $msidevs{$intrcfg->{name}} = {}; + } + $msidevs{$intrcfg->{name}}{$intrcfg->{ino}} = + \$stat{$cpu}{ivecs}{$cookie}; + } + } + + # All MSI interrupts of a device instance share a single MSI address. + # On X86 systems with an APIC, this MSI address is interpreted as CPU + # routing info by the APIC. For this reason, on these platforms, all + # interrupts for MSI devices must be moved to the same CPU at the same + # time. + # + # Since all interrupts will be on the same CPU on these platforms, all + # interrupts can be consolidated into one ivec entry. For such devices, + # num_ino will be > 1 to denote that a group move is needed. + + # Loop thru all MSI devices on X86 pcplusmp systems. + # Nop on other systems. + foreach my $msidevkey (sort keys %msidevs) { + + # Loop thru inos of the device, sorted by lowest value first + # For each cookie found for a device, incr num_ino for the + # lowest cookie and remove other cookies. + + # Assumes PIL is the same for first and current cookies + + my $first_ino = -1; + my $first_cookiep; + my $curr_cookiep; + foreach my $inokey (sort keys %{$msidevs{$msidevkey}}) { + $curr_cookiep = $msidevs{$msidevkey}{$inokey}; + if ($first_ino == -1) { + $first_ino = $inokey; + $first_cookiep = $curr_cookiep; + } else { + $$first_cookiep->{num_ino}++; + $$first_cookiep->{time} += + $$curr_cookiep->{time}; + if ($$curr_cookiep->{crtime} > + $$first_cookiep->{crtime}) { + $$first_cookiep->{crtime} = + $$curr_cookiep->{crtime}; + } + # Invalidate this cookie, less complicated and + # more efficient than deleting it. + $$curr_cookiep->{num_ino} = 0; + } + } } # We define the timerange as the amount of time spent gathering the @@ -287,10 +351,11 @@ sub getstat($) # ->{<ivec#>} iterates over ivecs for this cpu # ->{"time"} time used by this interrupt (in nsec) # ->{"pil"} pil level of this interrupt -# ->{"ino"} interrupt number +# ->{"ino"} interrupt number (or base vector if MSI group) # ->{"buspath"} filename of the directory of the device's bus # ->{"name"} device name # ->{"ihs"} number of different handlers sharing this ino +# ->{"num_ino"} number of interrupt vectors in MSI group # # It prints out the delta structure in a nice, human readable display. # @@ -403,9 +468,14 @@ sub generate_delta($$) } while (my ($inum, $newivec) = each %{$newcpst->{ivecs}}) { + + # Unused cookie, corresponding to an MSI vector which + # is part of a group. The whole group is accounted for + # by a different cookie. + next if ($newivec->{num_ino} == 0); + # If this ivec doesn't exist in $stat, or if $stat # shows a different crtime, set missing. - if (VERIFY(exists $cpst->{ivecs}{$inum} && $cpst->{ivecs}{$inum}{crtime} == $newivec->{crtime}, @@ -446,6 +516,7 @@ sub generate_delta($$) $dltivec{buspath} = $newivec->{buspath}; $dltivec{name} = $newivec->{name}; $dltivec{ihs} = $newivec->{ihs}; + $dltivec{num_ino} = $newivec->{num_ino}; } if ($delta{$cpu}{tot} < $delta{$cpu}{intrs}) { # Ewww! Hopefully just a rounding error. @@ -517,6 +588,7 @@ sub compress_deltas ($) $newivecs->{$inum}{buspath} = $ivec->{buspath}; $newivecs->{$inum}{name} = $ivec->{name}; $newivecs->{$inum}{ihs} = $ivec->{ihs}; + $newivecs->{$inum}{num_ino} = $ivec->{num_ino}; } } } @@ -868,7 +940,7 @@ sub do_reconfig($) next if ($ivec->{origcpu} == $cpuid); if (!intrmove($ivec->{buspath}, $ivec->{ino}, - $cpuid)) { + $cpuid, $ivec->{num_ino})) { syslog('warning', "Unable to move interrupts") if $warned++ == 0; syslog('debug', "Unable to move buspath ". @@ -1195,9 +1267,20 @@ if (!exists($ks->{pci_intrs})) { exit 0; } -my $stat = getstat($ks); +# See if this is a system with a pcplusmp APIC. +# Such systems will get special handling. +# Assume that if one bus has a pcplusmp APIC that they all do. +# Get a list of pci_intrs kstats. +my @elem = values(%{$ks->{pci_intrs}}); +my $elem0 = $elem[0]; +my $elemval = (values(%$elem0))[0]; +# Use its buspath to query the system. It is assumed that either all or none +# of the busses on a system are hosted by the pcplusmp APIC. +my $pcplusmp_sys = is_pcplusmp($elemval->{buspath}); + +my $stat = getstat($ks, $pcplusmp_sys); for (;;) { sub clear_deltas { @@ -1216,7 +1299,7 @@ for (;;) { } else { $ks = myks_update(); } - $newstat = getstat($ks); + $newstat = getstat($ks, $pcplusmp_sys); # $stat or $newstat could be zero if they're uninitialized, or if # getstat() failed. If $stat is zero, move $newstat to $stat, sleep @@ -1229,7 +1312,6 @@ for (;;) { next; } - # 2. Compare $newstat with the prior set of values, result in %$delta. $delta = generate_delta($stat, $newstat); diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.pm b/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.pm index 15dc274dd4..98cd3353b1 100644 --- a/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.pm +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.pm @@ -1,9 +1,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,7 +18,7 @@ # CDDL HEADER END # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -36,8 +35,8 @@ use DynaLoader; use vars qw($VERSION @ISA @EXPORT_OK); our @ISA = qw(Exporter DynaLoader); -our @EXPORT_OK = qw(intrmove); -our $VERSION = '0.01'; +our @EXPORT_OK = qw(intrmove is_pcplusmp); +our $VERSION = '0.02'; bootstrap Sun::Solaris::Intrs $VERSION; 1; diff --git a/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.xs b/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.xs index 0c2da8c480..0d563b7d5e 100644 --- a/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.xs +++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Intrs/Intrs.xs @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,39 +42,70 @@ #include "perl.h" #include "XSUB.h" +static int +open_dev(char *path) +{ + char intrpath[MAXPATHLEN]; + + (void) strcpy(intrpath, "/devices"); + (void) strcat(intrpath, path); + (void) strcat(intrpath, ":intr"); + return (open(intrpath, O_RDWR)); +} + MODULE = Sun::Solaris::Intrs PACKAGE = Sun::Solaris::Intrs PROTOTYPES: ENABLE int -intrmove(path, ino, cpu) +intrmove(path, ino, cpu, num_ino) char *path int ino int cpu + int num_ino INIT: - int i, ret; + int fd, ret; pcitool_intr_set_t iset; - static int fd = -1; - static char intrpath[MAXPATHLEN]; CODE: - if (fd == -1 || strcmp(path, intrpath)) { - (void) strcpy(intrpath, "/devices"); - (void) strcat(intrpath, path); - (void) strcat(intrpath, ":intr"); - if (fd != -1) - (void) close(fd); - fd = open(intrpath, O_RDONLY); - if (fd == -1) { - XSRETURN_UNDEF; - } + if ((fd = open_dev(path)) == -1) { + XSRETURN_UNDEF; } iset.ino = ino; iset.cpu_id = cpu; - iset.user_version = PCITOOL_USER_VERSION; + iset.flags = (num_ino > 1) ? PCITOOL_INTR_SET_FLAG_GROUP : 0; + iset.user_version = PCITOOL_VERSION; ret = ioctl(fd, PCITOOL_DEVICE_SET_INTR, &iset); if (ret == -1) { XSRETURN_UNDEF; } + (void) close(fd); XSRETURN_YES; + +int +is_pcplusmp(path) + char *path + + INIT: + int fd, ret; + pcitool_intr_info_t iinfo; + + CODE: + if ((fd = open_dev(path)) == -1) { + XSRETURN_UNDEF; + } + iinfo.user_version = PCITOOL_VERSION; + + ret = ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &iinfo); + (void) close(fd); + + if (ret == -1) { + XSRETURN_UNDEF; + } + + if (iinfo.ctlr_type == PCITOOL_CTLR_TYPE_PCPLUSMP) { + XSRETURN_YES; + } + + XSRETURN_NO; diff --git a/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c b/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c index 25845dd53e..34f406b109 100644 --- a/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c +++ b/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c @@ -117,7 +117,7 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) (void) memset(&pci_card, 0, sizeof (pci_card)); err = picl_get_propval_by_name(pcih, PICL_PROP_CLASSNAME, - piclclass, sizeof (piclclass)); + piclclass, sizeof (piclclass)); if (err != PICL_SUCCESS) /* Do not proceed to parse this branch */ @@ -133,7 +133,7 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) /* Do not proceed to parse this branch */ return (err); err = picl_get_propval_by_name(pcih, OBP_PROP_BOARD_NUM, &board, - sizeof (board)); + sizeof (board)); if (err == PICL_NORESPONSE) /* Do not proceed to parse this branch */ @@ -148,7 +148,7 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) } err = picl_get_propval_by_name - (pcih, OBP_PROP_PORTID, &portid, sizeof (portid)); + (pcih, OBP_PROP_PORTID, &portid, sizeof (portid)); if (err != PICL_PROPNOTFOUND) saved_portid = portid; @@ -169,7 +169,7 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) if (IS_EBUS(piclclass)) { err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, - &nodeh, sizeof (picl_nodehdl_t)); + &nodeh, sizeof (picl_nodehdl_t)); continue; } @@ -193,7 +193,7 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) */ err = picl_get_propinfo_by_name - (nodeh, OBP_PROP_REG, &pinfo, &proph); + (nodeh, OBP_PROP_REG, &pinfo, &proph); if (err == PICL_SUCCESS) { /* All of the array of bytes of "reg" have to be read */ reg_val = malloc(pinfo.size); @@ -212,27 +212,26 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) if (reg_val[0] != 0) { pci_card.dev_no = - (((reg_val[0]) & PCI_DEV_MASK) >> 11); + (((reg_val[0]) & PCI_DEV_MASK) >> 11); pci_card.func_no = - (((reg_val[0]) & PCI_FUNC_MASK) >> 8); + (((reg_val[0]) & PCI_FUNC_MASK) >> 8); pci_card.slot = - (((reg_val[0]) & PCI_BUS_MASK) >> 16); + (((reg_val[0]) & PCI_BUS_MASK) >> 16); } else free(reg_val); } - err = get_lane_width - (root_path, pci_card.slot, pci_card.dev_no, - pci_card.func_no, &actual, &maximum, &freq_max, - &freq_at, &bus_type); + err = get_lane_width(root_path, pci_card.slot, pci_card.dev_no, + pci_card.func_no, &actual, &maximum, &freq_max, &freq_at, + &bus_type); if (err != PICL_SUCCESS) { /* Move on to next node */ log_printf("Getting lane width failed for path %s\n", - pci_card.notes); + pci_card.notes); err = picl_get_propval_by_name - (nodeh, PICL_PROP_PEER, &nodeh, - sizeof (picl_nodehdl_t)); + (nodeh, PICL_PROP_PEER, &nodeh, + sizeof (picl_nodehdl_t)); continue; } @@ -292,29 +291,29 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) (void) strlcpy(pci_card.model, model, prop_size); if (bus_type == PCI) - (void) strlcpy - (pci_card.bus_type, "PCI", sizeof (pci_card.bus_type)); + (void) strlcpy(pci_card.bus_type, + "PCI", sizeof (pci_card.bus_type)); else if (bus_type == PCIX) - (void) strlcpy - (pci_card.bus_type, "PCIx", sizeof (pci_card.bus_type)); + (void) strlcpy(pci_card.bus_type, + "PCIx", sizeof (pci_card.bus_type)); else if (bus_type == PCIE) - (void) strlcpy - (pci_card.bus_type, "PCIe", sizeof (pci_card.bus_type)); + (void) strlcpy(pci_card.bus_type, + "PCIe", sizeof (pci_card.bus_type)); else - (void) strlcpy - (pci_card.bus_type, "UNKN", sizeof (pci_card.bus_type)); + (void) strlcpy(pci_card.bus_type, + "UNKN", sizeof (pci_card.bus_type)); /* Get revision id */ err = picl_get_propval_by_name - (nodeh, OBP_PROP_REVISION_ID, &rev_id, sizeof (rev_id)); + (nodeh, OBP_PROP_REVISION_ID, &rev_id, sizeof (rev_id)); /* Get device id */ err = picl_get_propval_by_name - (nodeh, OBP_PROP_DEVICE_ID, &dev_id, sizeof (dev_id)); + (nodeh, OBP_PROP_DEVICE_ID, &dev_id, sizeof (dev_id)); /* Get vendor id */ err = picl_get_propval_by_name - (nodeh, OBP_PROP_VENDOR_ID, &ven_id, sizeof (ven_id)); + (nodeh, OBP_PROP_VENDOR_ID, &ven_id, sizeof (ven_id)); /* * prtdiag -v prints all devices @@ -328,9 +327,8 @@ opl_pci_callback(picl_nodehdl_t pcih, void *args) log_printf("%-3d ", pci_card.schizo_portid); log_printf("%4x, %4x, %4x ", rev_id, dev_id, ven_id); - log_printf - ("%3d, %2d, %2d", - pci_card.slot, pci_card.dev_no, pci_card.func_no); + log_printf("%3d, %2d, %2d", + pci_card.slot, pci_card.dev_no, pci_card.func_no); /* Print status */ log_printf(" %-5.5s ", status); @@ -387,19 +385,19 @@ opl_display_pci(int syserrlog, picl_nodehdl_t plafh) log_printf("\n", 0); log_printf("\n", 0); log_printf(fmt, "", "IO", "", "", "", "", "Lane/Frq", - "", "", 0); + "", "", 0); log_printf("\n", 0); log_printf(fmt, "LSB", "Type", "LPID", " RvID,DvID,VnID", - " BDF", "State", "Act, Max", "Name", "Model", 0); + " BDF", "State", "Act, Max", "Name", "Model", 0); log_printf("\n"); - log_printf - (fmt, "---", "-----", "----", " ------------------", - " ---------", "-----", "-----------", - "------------------------------", - "--------------------", 0); + log_printf(fmt, + "---", "-----", "----", " ------------------", + " ---------", "-----", "-----------", + "------------------------------", + "--------------------", 0); log_printf("\n"); log_printf(fmt2, " Logical Path"); log_printf("\n"); @@ -429,7 +427,7 @@ opl_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE, &pinfo, &proph); if (err != PICL_SUCCESS) - return (err); + return (err); if (pinfo.type == PICL_PTYPE_CHARSTRING) { pval = malloc(pinfo.size); @@ -458,7 +456,7 @@ opl_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf) err = picl_get_propinfo(rowproph, &pinfo); if (err != PICL_SUCCESS) - return (err); + return (err); pval = malloc(pinfo.size); if (pval == NULL) @@ -485,7 +483,7 @@ do_piclinfo(int syserrlog) err = picl_initialize(); if (err != PICL_SUCCESS) { (void) log_printf("picl_initialize failed: %s\n", - picl_strerror(err)); + picl_strerror(err)); return (err); } @@ -493,7 +491,7 @@ do_piclinfo(int syserrlog) err = picl_get_root(&rooth); if (err != PICL_SUCCESS) { (void) log_printf("Getting root node failed: %s\n", - picl_strerror(err)); + picl_strerror(err)); return (err); } @@ -501,7 +499,7 @@ do_piclinfo(int syserrlog) if (err != PICL_SUCCESS) { (void) log_printf("Getting nodes by name failed: %s\n", - picl_strerror(err)); + picl_strerror(err)); return (err); } @@ -528,14 +526,14 @@ opl_get_node_by_name(picl_nodehdl_t rooth, char *name, return (PICL_FAILURE); err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh, - sizeof (picl_nodehdl_t)); + sizeof (picl_nodehdl_t)); while (err == PICL_SUCCESS) { err = picl_get_propval_by_name(childh, PICL_PROP_NAME, - nodename, (strlen(name) + 1)); + nodename, (strlen(name) + 1)); if (err != PICL_SUCCESS) { err = picl_get_propval_by_name(childh, PICL_PROP_PEER, - &childh, sizeof (picl_nodehdl_t)); + &childh, sizeof (picl_nodehdl_t)); continue; } @@ -545,7 +543,7 @@ opl_get_node_by_name(picl_nodehdl_t rooth, char *name, } err = picl_get_propval_by_name(childh, PICL_PROP_PEER, - &childh, sizeof (picl_nodehdl_t)); + &childh, sizeof (picl_nodehdl_t)); } return (err); @@ -579,7 +577,7 @@ read_long(int fd, int bus, int dev, int func, int offset, int *ret) int rval; pcitool_reg_t prg; - prg.user_version = PCITOOL_USER_VERSION; + prg.user_version = PCITOOL_VERSION; prg.barnum = 0; prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + PCITOOL_ACC_ATTR_ENDN_LTL; @@ -589,10 +587,8 @@ read_long(int fd, int bus, int dev, int func, int offset, int *ret) prg.offset = offset; rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg); if (rval != 0) { - log_printf - ("DEV_GET failed %d %s\n", rval, strerror(errno)); - log_printf - ("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); + log_printf("DEV_GET failed %d %s\n", rval, strerror(errno)); + log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); } *ret = rval; return ((uint32_t)prg.data); @@ -604,7 +600,7 @@ read_word(int fd, int bus, int dev, int func, int offset, int *ret) int rval; pcitool_reg_t prg; - prg.user_version = PCITOOL_USER_VERSION; + prg.user_version = PCITOOL_VERSION; prg.barnum = 0; prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + PCITOOL_ACC_ATTR_ENDN_LTL; @@ -614,10 +610,8 @@ read_word(int fd, int bus, int dev, int func, int offset, int *ret) prg.offset = offset; rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg); if (rval != 0) { - log_printf - ("DEV_GET failed %d %s\n", rval, strerror(errno)); - log_printf - ("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); + log_printf("DEV_GET failed %d %s\n", rval, strerror(errno)); + log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); } *ret = rval; return ((uint16_t)prg.data); @@ -629,7 +623,7 @@ read_byte(int fd, int bus, int dev, int func, int offset, int *ret) int rval; pcitool_reg_t prg; - prg.user_version = PCITOOL_USER_VERSION; + prg.user_version = PCITOOL_VERSION; prg.barnum = 0; prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + PCITOOL_ACC_ATTR_ENDN_LTL; @@ -639,10 +633,8 @@ read_byte(int fd, int bus, int dev, int func, int offset, int *ret) prg.offset = offset; rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg); if (rval != 0) { - log_printf - ("DEV_GET failed %d %s\n", rval, strerror(errno)); - log_printf - ("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); + log_printf("DEV_GET failed %d %s\n", rval, strerror(errno)); + log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset); } *ret = rval; return ((uint8_t)prg.data); @@ -700,15 +692,15 @@ get_lane_width if (ret != 0) { return (PICL_FAILURE); } - link_status = read_word(fd, bus, dev, func, cap_ptr + - PCIE_LINKSTS, &ret); + link_status = read_word(fd, bus, dev, func, + cap_ptr + PCIE_LINKSTS, &ret); if (ret != 0) { return (PICL_FAILURE); } *actual = ((link_status >> PCI_LINK_SHIFT) & - PCI_LINK_MASK); + PCI_LINK_MASK); *maximum = ((link_cap >> PCI_LINK_SHIFT) & - PCI_LINK_MASK); + PCI_LINK_MASK); *type = PCIE; } if (capid == PCI_CAP_ID_PCIX) { @@ -717,7 +709,7 @@ get_lane_width int max_speed = PCI_FREQ_66; hdr_type = read_byte - (fd, bus, dev, func, PCI_CONF_HEADER, &ret); + (fd, bus, dev, func, PCI_CONF_HEADER, &ret); if (ret != 0) { /* ioctl failure */ return (PICL_FAILURE); @@ -726,7 +718,7 @@ get_lane_width /* This is a PCI-X bridge */ uint16_t sec_status, mode; sec_status = read_word(fd, bus, dev, func, - cap_ptr + PCI_PCIX_SEC_STATUS, &ret); + cap_ptr + PCI_PCIX_SEC_STATUS, &ret); if (ret != 0) { /* ioctl failure */ return (PICL_FAILURE); @@ -739,8 +731,8 @@ get_lane_width max_speed = PCI_FREQ_533; *speed_max = max_speed; *type = PCIX; - mode = (sec_status >> PCI_CLASS_BRIDGE) - & PCI_BRIDGE_MC; + mode = (sec_status >> PCI_CLASS_BRIDGE) & + PCI_BRIDGE_MC; if (mode) { int speed; if (mode == PCI_MODE_66) @@ -760,13 +752,13 @@ get_lane_width return (PICL_FAILURE); } if (pcix_status & - (PCI_LEAF_ULONG << PCI_SHIFT_133)) + (PCI_LEAF_ULONG << PCI_SHIFT_133)) max_speed = PCI_FREQ_133; if (pcix_status & - (PCI_LEAF_ULONG << PCI_SHIFT_266)) + (PCI_LEAF_ULONG << PCI_SHIFT_266)) max_speed = PCI_FREQ_266; if (pcix_status & - (PCI_LEAF_ULONG << PCI_SHIFT_533)) + (PCI_LEAF_ULONG << PCI_SHIFT_533)) max_speed = PCI_FREQ_533; *speed_max = max_speed; *type = PCI; @@ -823,7 +815,7 @@ picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret) * If it is not an int or uint prop, return failure */ if ((pinfo.type != PICL_PTYPE_INT) && - (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { + (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) { *ret = PICL_FAILURE; return (0); } @@ -866,21 +858,21 @@ do_walk(picl_nodehdl_t rooth, const char *classname, char classval[PICL_CLASSNAMELEN_MAX]; err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh, - sizeof (chdh)); + sizeof (chdh)); while (err == PICL_SUCCESS) { err = picl_get_propval_by_name(chdh, PICL_PROP_NAME, - classval, sizeof (classval)); + classval, sizeof (classval)); if (err != PICL_SUCCESS) return (err); err = callback_fn(chdh, c_args); if ((err = do_walk(chdh, classname, c_args, callback_fn)) != - PICL_WALK_CONTINUE) + PICL_WALK_CONTINUE) return (err); err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh, - sizeof (chdh)); + sizeof (chdh)); } if (err == PICL_PROPNOTFOUND) /* end of a branch */ return (PICL_WALK_CONTINUE); diff --git a/usr/src/uts/common/sys/pci_tools.h b/usr/src/uts/common/sys/pci_tools.h index 5e581074ef..2e9a95aa71 100644 --- a/usr/src/uts/common/sys/pci_tools.h +++ b/usr/src/uts/common/sys/pci_tools.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,11 +35,11 @@ extern "C" { #endif /* - * Versioning. Have different versions for userland program and drivers, so - * they can all stay in sync with each other. + * Versioning. */ -#define PCITOOL_USER_VERSION 1 -#define PCITOOL_DRVR_VERSION 1 +#define PCITOOL_V1 1 +#define PCITOOL_V2 2 +#define PCITOOL_VERSION PCITOOL_V2 /* File suffixes for nexus pcitool nodes. */ #define PCI_MINOR_REG "reg" @@ -52,20 +51,19 @@ extern "C" { #define PCITOOL_IOC (('P' << 24) | ('C' << 16) | ('T' << 8)) /* Read/write a device on a PCI bus, in physical space. */ -#define PCITOOL_DEVICE_GET_REG (PCITOOL_IOC | 1) -#define PCITOOL_DEVICE_SET_REG (PCITOOL_IOC | 2) +#define PCITOOL_DEVICE_GET_REG (PCITOOL_IOC | 1) +#define PCITOOL_DEVICE_SET_REG (PCITOOL_IOC | 2) /* Read/write the PCI nexus bridge, in physical space. */ -#define PCITOOL_NEXUS_GET_REG (PCITOOL_IOC | 3) -#define PCITOOL_NEXUS_SET_REG (PCITOOL_IOC | 4) +#define PCITOOL_NEXUS_GET_REG (PCITOOL_IOC | 3) +#define PCITOOL_NEXUS_SET_REG (PCITOOL_IOC | 4) /* Get/set interrupt-CPU mapping for PCI devices. */ -#define PCITOOL_DEVICE_GET_INTR (PCITOOL_IOC | 5) -#define PCITOOL_DEVICE_SET_INTR (PCITOOL_IOC | 6) - -/* Return the number of supported interrupts on a PCI bus. */ -#define PCITOOL_DEVICE_NUM_INTR (PCITOOL_IOC | 7) +#define PCITOOL_DEVICE_GET_INTR (PCITOOL_IOC | 5) +#define PCITOOL_DEVICE_SET_INTR (PCITOOL_IOC | 6) +/* Get system interrupt information */ +#define PCITOOL_SYSTEM_INTR_INFO (PCITOOL_IOC | 8) /* * This file contains data structures for the pci tool. @@ -131,8 +129,14 @@ typedef struct pcitool_intr_set { uint32_t ino; /* interrupt to set - to kernel */ uint32_t cpu_id; /* to: cpu to set / from: old cpu returned */ pcitool_errno_t status; /* from kernel */ + uint32_t flags; /* to kernel */ } pcitool_intr_set_t; +/* + * flags for pcitool_intr_set_t + */ +#define PCITOOL_INTR_SET_FLAG_GROUP 0x1 + /* * PCITOOL_DEVICE_GET_INTR ioctl data structure to dump out the @@ -145,7 +149,6 @@ typedef struct pcitool_intr_dev { char path[MAXPATHLEN]; /* device path - from kernel */ } pcitool_intr_dev_t; - typedef struct pcitool_intr_get { uint16_t user_version; /* Userland program version - to krnl */ uint16_t drvr_version; /* Driver version - from kernel */ @@ -171,6 +174,22 @@ typedef struct pcitool_intr_get { sizeof (pcitool_intr_dev_t) + \ (num_devs * sizeof (pcitool_intr_dev_t))) +typedef struct pcitool_intr_info { + uint16_t user_version; /* Userland program version - to krnl */ + uint16_t drvr_version; /* Driver version - from kernel */ + uint32_t num_intr; /* Number of intrs suppt by nexus */ + uint32_t ctlr_version; /* Intr ctlr HW version - from kernel */ + uchar_t ctlr_type; /* A PCITOOL_CTLR_TYPE - from kernel */ +} pcitool_intr_info_t; + +/* + * Interrupt controller types + */ +#define PCITOOL_CTLR_TYPE_UNKNOWN 0 +#define PCITOOL_CTLR_TYPE_RISC 1 +#define PCITOOL_CTLR_TYPE_UPPC 2 +#define PCITOOL_CTLR_TYPE_PCPLUSMP 3 + /* * Size and endian fields for acc_attr bitmask. */ diff --git a/usr/src/uts/i86pc/io/mp_platform_common.c b/usr/src/uts/i86pc/io/mp_platform_common.c index acb2d0071d..1a131b619f 100644 --- a/usr/src/uts/i86pc/io/mp_platform_common.c +++ b/usr/src/uts/i86pc/io/mp_platform_common.c @@ -1101,6 +1101,35 @@ apic_cpu_in_range(int cpu) return ((cpu & ~IRQ_USER_BOUND) < apic_nproc); } +uint16_t +apic_get_apic_version() +{ + int i; + uchar_t min_io_apic_ver = 0; + static uint16_t version; /* Cache as value is constant */ + static boolean_t found = B_FALSE; /* Accomodate zero version */ + + if (found == B_FALSE) { + found = B_TRUE; + + /* + * Don't assume all IO APICs in the system are the same. + * + * Set to the minimum version. + */ + for (i = 0; i < apic_io_max; i++) { + if ((apic_io_ver[i] != 0) && + ((min_io_apic_ver == 0) || + (min_io_apic_ver >= apic_io_ver[i]))) + min_io_apic_ver = apic_io_ver[i]; + } + + /* Assume all local APICs are of the same version. */ + version = (min_io_apic_ver << 8) | apic_cpus[0].aci_local_ver; + } + return (version); +} + static struct apic_mpfps_hdr * apic_find_fps_sig(caddr_t cptr, int len) { @@ -2766,32 +2795,36 @@ apic_rebind(apic_irq_t *irq_ptr, int bind_cpu, return (0); } - } - /* - * NOTE: We do not unmask the RDT here, as an interrupt MAY still - * come in before we have a chance to reprogram it below. The - * reprogramming below will simultaneously change and unmask the - * RDT entry. - */ + /* + * NOTE: We do not unmask the RDT here, as an interrupt MAY + * still come in before we have a chance to reprogram it below. + * The reprogramming below will simultaneously change and + * unmask the RDT entry. + */ - if ((uchar_t)bind_cpu == IRQ_UNBOUND) { + if ((uchar_t)bind_cpu == IRQ_UNBOUND) { + rdt_entry = AV_LDEST | AV_LOPRI | + irq_ptr->airq_rdt_entry; - rdt_entry = AV_LDEST | AV_LOPRI | irq_ptr->airq_rdt_entry; + /* Write the RDT entry -- no specific CPU binding */ + WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapicindex, intin_no, + AV_TOALL); - /* Write the RDT entry -- no specific CPU binding */ - WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapicindex, intin_no, - AV_TOALL); + if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != + IRQ_UNBOUND) + apic_cpus[airq_temp_cpu].aci_temp_bound--; - if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != IRQ_UNBOUND) - apic_cpus[airq_temp_cpu].aci_temp_bound--; - - /* Write the vector, trigger, and polarity portion of the RDT */ - WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapicindex, intin_no, - rdt_entry); + /* + * Write the vector, trigger, and polarity portion of + * the RDT + */ + WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapicindex, intin_no, + rdt_entry); - irq_ptr->airq_temp_cpu = IRQ_UNBOUND; - return (0); + irq_ptr->airq_temp_cpu = IRQ_UNBOUND; + return (0); + } } if (bind_cpu & IRQ_USER_BOUND) { diff --git a/usr/src/uts/i86pc/io/pci/pci_common.c b/usr/src/uts/i86pc/io/pci/pci_common.c index cacc0ae123..a5bdc5afa5 100644 --- a/usr/src/uts/i86pc/io/pci/pci_common.c +++ b/usr/src/uts/i86pc/io/pci/pci_common.c @@ -145,7 +145,7 @@ pci_common_set_parent_private_data(dev_info_t *dip) pdptr = (struct ddi_parent_private_data *)kmem_zalloc( (sizeof (struct ddi_parent_private_data) + - sizeof (struct intrspec)), KM_SLEEP); + sizeof (struct intrspec)), KM_SLEEP); pdptr->par_intr = (struct intrspec *)(pdptr + 1); pdptr->par_nintr = 1; ddi_set_parent_data(dip, pdptr); @@ -400,7 +400,7 @@ SUPPORTED_TYPES_OUT: if (pciepci) { /* update priority in ispec */ isp = pci_intx_get_ispec(pdip, rdip, - (int)hdlp->ih_inum); + (int)hdlp->ih_inum); ispec = (struct intrspec *)isp; if (ispec) ispec->intrspec_pri = hdlp->ih_pri; @@ -437,7 +437,7 @@ SUPPORTED_TYPES_OUT: msix_p = i_ddi_get_msix(hdlp->ih_dip); if (msix_p && (i_ddi_intr_get_current_nintrs( - hdlp->ih_dip) - 1) == 0) { + hdlp->ih_dip) - 1) == 0) { pci_msix_fini(msix_p); i_ddi_set_msix(hdlp->ih_dip, NULL); } @@ -581,8 +581,9 @@ SUPPORTED_TYPES_OUT: DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: " "pci_enable_intr failed for %d\n", i)); for (j = 0; j < i; j++) { - hdlp = (ddi_intr_handle_impl_t *)h_array[j]; - pci_disable_intr(pdip, rdip, hdlp, + hdlp = (ddi_intr_handle_impl_t *) + h_array[j]; + pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum); } return (DDI_FAILURE); @@ -899,7 +900,7 @@ pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, /*FALLTHRU*/ /* These require no special privileges. */ case PCITOOL_DEVICE_GET_INTR: - case PCITOOL_DEVICE_NUM_INTR: + case PCITOOL_SYSTEM_INTR_INFO: rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode); break; } @@ -1599,9 +1600,9 @@ is_amd_northbridge(dev_info_t *dip) int vendor_id, device_id; vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "vendor-id", -1); + "vendor-id", -1); device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "device-id", -1); + "device-id", -1); if (IS_AMD_NTBRIDGE(vendor_id, device_id)) return (0); diff --git a/usr/src/uts/i86pc/io/pci/pci_kstats.c b/usr/src/uts/i86pc/io/pci/pci_kstats.c index c8032f1286..a387ba1ff8 100644 --- a/usr/src/uts/i86pc/io/pci/pci_kstats.c +++ b/usr/src/uts/i86pc/io/pci/pci_kstats.c @@ -72,13 +72,12 @@ static kmutex_t pci_ks_template_lock; static int pci_ih_ks_update(kstat_t *ksp, int rw) { - pci_kstat_private_t *private_data = - (pci_kstat_private_t *)ksp->ks_private; - dev_info_t *rootnex_dip = private_data->rootnex_dip; - ddi_intr_handle_impl_t *ih_p = private_data->hdlp; - dev_info_t *dip = ih_p->ih_dip; - int maxlen = - sizeof (pci_ks_template.ihks_name.value.c); + pci_kstat_private_t *private_data = + (pci_kstat_private_t *)ksp->ks_private; + dev_info_t *rootnex_dip = private_data->rootnex_dip; + ddi_intr_handle_impl_t *ih_p = private_data->hdlp; + dev_info_t *dip = ih_p->ih_dip; + int maxlen = sizeof (pci_ks_template.ihks_name.value.c); apic_get_intr_t intrinfo; (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d", @@ -112,7 +111,7 @@ pci_ih_ks_update(kstat_t *ksp, int rw) intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR; if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) || (pci_get_intr_from_vecirq(&intrinfo, ih_p->ih_vector, IS_IRQ) != - DDI_SUCCESS) || + DDI_SUCCESS) || (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) { (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled"); @@ -134,8 +133,17 @@ pci_ih_ks_update(kstat_t *ksp, int rw) * Interrupt is valid (not a dummy), not user-bound to a specific cpu, * and enabled. Update kstat fields. */ - (void) strcpy(pci_ks_template.ihks_type.value.c, - DDI_INTR_IS_MSI_OR_MSIX(ih_p->ih_type) ? "msi" : "fixed"); + switch (ih_p->ih_type) { + case DDI_INTR_TYPE_MSI: + (void) strcpy(pci_ks_template.ihks_type.value.c, "msi"); + break; + case DDI_INTR_TYPE_MSIX: + (void) strcpy(pci_ks_template.ihks_type.value.c, "msix"); + break; + default: + (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed"); + break; + } pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri; pci_ks_template.ihks_time.value.ui64 = ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks; @@ -184,8 +192,8 @@ void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip, void pci_kstat_delete(kstat_t *ksp) { - pci_kstat_private_t *kstat_private; - ddi_intr_handle_impl_t *hdlp; + pci_kstat_private_t *kstat_private; + ddi_intr_handle_impl_t *hdlp; if (ksp) { kstat_private = ksp->ks_private; diff --git a/usr/src/uts/i86pc/io/pci/pci_tools.c b/usr/src/uts/i86pc/io/pci/pci_tools.c index 7e4c90c432..7d35c9724b 100644 --- a/usr/src/uts/i86pc/io/pci/pci_tools.c +++ b/usr/src/uts/i86pc/io/pci/pci_tools.c @@ -84,7 +84,7 @@ static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); -/* Extern decalrations */ +/* Extern declarations */ extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, psm_intr_op_t, int *); @@ -121,21 +121,6 @@ pcitool_uninit(dev_info_t *dip) ddi_remove_minor_node(dip, PCI_MINOR_REG); } - -/* Return the number of interrupts on a pci bus. */ -static int -pcitool_intr_get_max_ino(uint32_t *arg, int mode) -{ - uint32_t num_intr = APIC_MAX_VECTOR; - - if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) != - DDI_SUCCESS) - return (EFAULT); - else - return (SUCCESS); -} - - /*ARGSUSED*/ static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode) @@ -144,12 +129,31 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode) pcitool_intr_set_t iset; uint32_t old_cpu; int ret, result; + size_t copyinout_size; int rval = SUCCESS; - if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + /* Version 1 of pcitool_intr_set_t doesn't have flags. */ + copyinout_size = (size_t)&iset.flags - (size_t)&iset; + + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) return (EFAULT); + switch (iset.user_version) { + case PCITOOL_V1: + break; + + case PCITOOL_V2: + copyinout_size = sizeof (pcitool_intr_set_t); + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) + return (EFAULT); + break; + + default: + iset.status = PCITOOL_OUT_OF_RANGE; + rval = ENOTSUP; + goto done_set_intr; + } + if (iset.ino > APIC_MAX_VECTOR) { rval = EINVAL; iset.status = PCITOOL_INVALID_INO; @@ -164,6 +168,7 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode) goto done_set_intr; } + old_cpu &= ~PSMGI_CPU_USER_BOUND; /* @@ -172,9 +177,20 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode) */ info_hdl.ih_vector = iset.ino; info_hdl.ih_private = (void *)(uintptr_t)iset.cpu_id; - ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_SET_CPU, &result); + if (pcitool_debug) + prom_printf("user version:%d, flags:0x%x\n", + iset.user_version, iset.flags); + + result = ENOTSUP; + if ((iset.user_version >= PCITOOL_V2) && + (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP)) { + ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_GRP_SET_CPU, + &result); + } else { + ret = (*psm_intr_ops)(NULL, &info_hdl, PSM_INTR_OP_SET_CPU, + &result); + } - iset.drvr_version = PCITOOL_DRVR_VERSION; if (ret != PSM_SUCCESS) { switch (result) { case EIO: /* Error making the change */ @@ -189,6 +205,10 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode) rval = EINVAL; iset.status = PCITOOL_INVALID_CPUID; break; + case ENOTSUP: /* Requested PSM intr ops missing */ + rval = ENOTSUP; + iset.status = PCITOOL_IO_ERROR; + break; } } @@ -196,8 +216,8 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode) iset.cpu_id = old_cpu; done_set_intr: - if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + iset.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) rval = EFAULT; return (rval); } @@ -337,7 +357,7 @@ done_get_intr: ndi_devi_exit(dip, circ); } - iget->drvr_version = PCITOOL_DRVR_VERSION; + iget->drvr_version = PCITOOL_VERSION; copyout_rval = ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); @@ -350,6 +370,50 @@ done_get_intr: return (rval); } +/*ARGSUSED*/ +static int +pcitool_intr_info(dev_info_t *dip, void *arg, int mode) +{ + pcitool_intr_info_t intr_info; + ddi_intr_handle_impl_t info_hdl; + int rval = SUCCESS; + + /* If we need user_version, and to ret same user version as passed in */ + if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != + DDI_SUCCESS) { + if (pcitool_debug) + prom_printf("Error reading arguments\n"); + return (EFAULT); + } + + /* For UPPC systems, psm_intr_ops has no entry for APIC_TYPE. */ + if ((rval = (*psm_intr_ops)(NULL, &info_hdl, + PSM_INTR_OP_APIC_TYPE, NULL)) != PSM_SUCCESS) { + intr_info.ctlr_type = PCITOOL_CTLR_TYPE_UPPC; + intr_info.ctlr_version = 0; + + } else { + intr_info.ctlr_version = (uint32_t)info_hdl.ih_ver; + if (strcmp((char *)info_hdl.ih_private, + APIC_PCPLUSMP_NAME) == 0) + intr_info.ctlr_type = PCITOOL_CTLR_TYPE_PCPLUSMP; + else + intr_info.ctlr_type = PCITOOL_CTLR_TYPE_UNKNOWN; + } + + intr_info.num_intr = APIC_MAX_VECTOR; + intr_info.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != + DDI_SUCCESS) { + if (pcitool_debug) + prom_printf("Error returning arguments.\n"); + rval = EFAULT; + } + + return (rval); +} + + /* * Main function for handling interrupt CPU binding requests and queries. @@ -372,8 +436,8 @@ pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode) rval = pcitool_get_intr(dip, arg, mode); break; - case PCITOOL_DEVICE_NUM_INTR: - rval = pcitool_intr_get_max_ino(arg, mode); + case PCITOOL_SYSTEM_INTR_INFO: + rval = pcitool_intr_info(dip, arg, mode); break; default: @@ -571,7 +635,7 @@ pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) no_trap(); if (pcitool_debug) prom_printf( - "pcitool_mem_access: on_trap caught an error...\n"); + "pcitool_io_access: on_trap caught an error...\n"); prg->status = PCITOOL_INVALID_ADDRESS; return (EFAULT); } @@ -1075,6 +1139,7 @@ pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) pcitool_unmap(virt_addr, num_virt_pages); } done_reg: + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { if (pcitool_debug) diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic.c b/usr/src/uts/i86pc/io/pcplusmp/apic.c index f51d4adcfd..55f168a4ac 100644 --- a/usr/src/uts/i86pc/io/pcplusmp/apic.c +++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c @@ -246,7 +246,7 @@ static struct psm_info apic_psm_info = { PSM_INFO_VER01_5, /* version */ PSM_OWN_EXCLUSIVE, /* ownership */ (struct psm_ops *)&apic_ops, /* operation */ - "pcplusmp", /* machine name */ + APIC_PCPLUSMP_NAME, /* machine name */ "pcplusmp v1.4 compatible %I%", }; @@ -2013,7 +2013,7 @@ apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type, irqptr->airq_major = major; if (i == 0) /* they all bound to the same cpu */ cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno, - 0xff, 0xff); + 0xff, 0xff); else irqptr->airq_cpu = cpu; DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: irq=0x%x " @@ -2155,3 +2155,9 @@ apic_modify_vector(uchar_t vector, int irq) apic_vector_to_irq[vector] = (uchar_t)irq; return (vector); } + +char * +apic_get_apic_type() +{ + return (apic_psm_info.p_mach_idstring); +} diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c b/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c index 324c8cb54d..81325f4930 100644 --- a/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c +++ b/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c @@ -95,7 +95,7 @@ apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, /* MSI Address */ msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | - (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); + (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); /* MSI Data: MSI is edge triggered according to spec */ msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); @@ -163,7 +163,7 @@ apic_navail_vector(dev_info_t *dip, int pri) for (i = lowest; i < highest; i++) { count = 0; while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && - (i < highest)) { + (i < highest)) { if (APIC_CHECK_RESERVE_VECTORS(i)) break; count++; @@ -212,7 +212,7 @@ apic_find_multi_vectors(int pri, int count) i = (i + msibits) & ~msibits; start = i; while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && - (i < highest)) { + (i < highest)) { if (APIC_CHECK_RESERVE_VECTORS(i)) break; navail++; @@ -433,107 +433,6 @@ apic_check_msi_support() return (PSM_FAILURE); } -int -apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) -{ - struct autovec *av_dev; - uchar_t irqno; - int i; - apic_irq_t *irq_p; - - /* Sanity check the vector/irq argument. */ - ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); - - mutex_enter(&airq_mutex); - - /* - * Convert the vecirq arg to an irq using vector_to_irq table - * if the arg is a vector. Pass thru if already an irq. - */ - if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == - PSMGI_INTRBY_VEC) - irqno = apic_vector_to_irq[vecirq]; - else - irqno = vecirq; - - irq_p = apic_irq_table[irqno]; - - if ((irq_p == NULL) || - (irq_p->airq_temp_cpu == IRQ_UNBOUND) || - (irq_p->airq_temp_cpu == IRQ_UNINIT)) { - mutex_exit(&airq_mutex); - return (PSM_FAILURE); - } - - if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { - - /* Get the (temp) cpu from apic_irq table, indexed by irq. */ - intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; - - /* Return user bound info for intrd. */ - if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { - intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; - intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; - } - } - - if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) { - intr_params_p->avgi_vector = irq_p->airq_vector; - } - - if (intr_params_p->avgi_req_flags & - (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) { - /* Get number of devices from apic_irq table shared field. */ - intr_params_p->avgi_num_devs = irq_p->airq_share; - } - - if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { - - intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; - - /* Some devices have NULL dip. Don't count these. */ - if (intr_params_p->avgi_num_devs > 0) { - for (i = 0, av_dev = autovect[irqno].avh_link; - av_dev; av_dev = av_dev->av_link) - if (av_dev->av_vector && av_dev->av_dip) - i++; - intr_params_p->avgi_num_devs = - MIN(intr_params_p->avgi_num_devs, i); - } - - /* There are no viable dips to return. */ - if (intr_params_p->avgi_num_devs == 0) - intr_params_p->avgi_dip_list = NULL; - - else { /* Return list of dips */ - - /* Allocate space in array for that number of devs. */ - intr_params_p->avgi_dip_list = kmem_zalloc( - intr_params_p->avgi_num_devs * - sizeof (dev_info_t *), - KM_SLEEP); - - /* - * Loop through the device list of the autovec table - * filling in the dip array. - * - * Note that the autovect table may have some special - * entries which contain NULL dips. These will be - * ignored. - */ - for (i = 0, av_dev = autovect[irqno].avh_link; - av_dev; av_dev = av_dev->av_link) - if (av_dev->av_vector && av_dev->av_dip) - intr_params_p->avgi_dip_list[i++] = - av_dev->av_dip; - } - } - - mutex_exit(&airq_mutex); - - return (PSM_SUCCESS); -} - /* * apic_pci_msi_unconfigure: * @@ -676,6 +575,282 @@ apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) } +static int +apic_set_cpu(uint32_t vector, int cpu, int *result) +{ + apic_irq_t *irqp; + int iflag; + int ret; + + DDI_INTR_IMPLDBG((CE_CONT, "APIC_SET_CPU\n")); + + /* Convert the vector to the irq using vector_to_irq table. */ + mutex_enter(&airq_mutex); + irqp = apic_irq_table[apic_vector_to_irq[vector]]; + mutex_exit(&airq_mutex); + + if (irqp == NULL) { + *result = ENXIO; + return (PSM_FAILURE); + } + + /* Fail if this is an MSI intr and is part of a group. */ + if ((irqp->airq_mps_intr_index == MSI_INDEX) && + (irqp->airq_intin_no > 1)) { + *result = ENXIO; + return (PSM_FAILURE); + } + + iflag = intr_clear(); + lock_set(&apic_ioapic_lock); + + ret = apic_rebind_all(irqp, cpu); + + lock_clear(&apic_ioapic_lock); + intr_restore(iflag); + + if (ret) { + *result = EIO; + return (PSM_FAILURE); + } + *result = 0; + return (PSM_SUCCESS); +} + +static int +apic_grp_set_cpu(uint32_t vector, int new_cpu, int *result) +{ + dev_info_t *orig_dip; + uchar_t orig_cpu; + int iflag; + apic_irq_t *irqps[PCI_MSI_MAX_INTRS]; + int i; + int cap_ptr; + int msi_mask_off; + ushort_t msi_ctrl; + uint32_t msi_pvm; + ddi_acc_handle_t handle; + int num_vectors = 0; + + DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n")); + + /* + * Take mutex to insure that table doesn't change out from underneath + * us while we're playing with it. + */ + mutex_enter(&airq_mutex); + irqps[0] = apic_irq_table[apic_vector_to_irq[vector]]; + orig_cpu = irqps[0]->airq_temp_cpu; + orig_dip = irqps[0]->airq_dip; + num_vectors = irqps[0]->airq_intin_no; + + /* A "group" of 1 */ + if (num_vectors == 1) { + mutex_exit(&airq_mutex); + return (apic_set_cpu(vector, new_cpu, result)); + } + + *result = ENXIO; + + if (irqps[0]->airq_mps_intr_index != MSI_INDEX) { + mutex_exit(&airq_mutex); + DDI_INTR_IMPLDBG((CE_CONT, "set_grp: intr not MSI\n")); + goto set_grp_intr_done; + } + if ((num_vectors < 1) || ((num_vectors - 1) & vector)) { + mutex_exit(&airq_mutex); + DDI_INTR_IMPLDBG((CE_CONT, + "set_grp: base vec not part of a grp or not aligned: " + "vec:0x%x, num_vec:0x%x\n", vector, num_vectors)); + goto set_grp_intr_done; + } + DDI_INTR_IMPLDBG((CE_CONT, "set_grp: num intrs in grp: %d\n", + num_vectors)); + + ASSERT((num_vectors + vector) < APIC_MAX_VECTOR); + + *result = EIO; + + /* + * All IRQ entries in the table for the given device will be not + * shared. Since they are not shared, the dip in the table will + * be true to the device of interest. + */ + for (i = 1; i < num_vectors; i++) { + irqps[i] = apic_irq_table[apic_vector_to_irq[vector + i]]; + if (irqps[i] == NULL) { + mutex_exit(&airq_mutex); + goto set_grp_intr_done; + } +#ifdef DEBUG + /* Sanity check: CPU and dip is the same for all entries. */ + if ((irqps[i]->airq_dip != orig_dip) || + (irqps[i]->airq_temp_cpu != orig_cpu)) { + mutex_exit(&airq_mutex); + DDI_INTR_IMPLDBG((CE_CONT, + "set_grp: cpu or dip for vec 0x%x difft than for " + "vec 0x%x\n", vector, vector + i)); + DDI_INTR_IMPLDBG((CE_CONT, + " cpu: %d vs %d, dip: 0x%p vs 0x%p\n", orig_cpu, + irqps[i]->airq_temp_cpu, (void *)orig_dip, + (void *)irqps[i]->airq_dip)); + goto set_grp_intr_done; + } +#endif /* DEBUG */ + } + mutex_exit(&airq_mutex); + + cap_ptr = i_ddi_get_msi_msix_cap_ptr(orig_dip); + handle = i_ddi_get_pci_config_handle(orig_dip); + msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); + + /* MSI Per vector masking is supported. */ + if (msi_ctrl & PCI_MSI_PVM_MASK) { + if (msi_ctrl & PCI_MSI_64BIT_MASK) + msi_mask_off = cap_ptr + PCI_MSI_64BIT_MASKBITS; + else + msi_mask_off = cap_ptr + PCI_MSI_32BIT_MASK; + msi_pvm = pci_config_get32(handle, msi_mask_off); + pci_config_put32(handle, msi_mask_off, (uint32_t)-1); + DDI_INTR_IMPLDBG((CE_CONT, + "set_grp: pvm supported. Mask set to 0x%x\n", + pci_config_get32(handle, msi_mask_off))); + } + + iflag = intr_clear(); + lock_set(&apic_ioapic_lock); + + /* + * Do the first rebind and check for errors. Apic_rebind_all returns + * an error if the CPU is not accepting interrupts. If the first one + * succeeds they all will. + */ + if (apic_rebind_all(irqps[0], new_cpu)) + (void) apic_rebind_all(irqps[0], orig_cpu); + else { + for (i = 1; i < num_vectors; i++) + (void) apic_rebind_all(irqps[i], new_cpu); + *result = 0; /* SUCCESS */ + } + + lock_clear(&apic_ioapic_lock); + intr_restore(iflag); + + /* Reenable vectors if per vector masking is supported. */ + if (msi_ctrl & PCI_MSI_PVM_MASK) { + pci_config_put32(handle, msi_mask_off, msi_pvm); + DDI_INTR_IMPLDBG((CE_CONT, + "set_grp: pvm supported. Mask restored to 0x%x\n", + pci_config_get32(handle, msi_mask_off))); + } + +set_grp_intr_done: + if (*result != 0) + return (PSM_FAILURE); + + return (PSM_SUCCESS); +} + +static int +apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) +{ + struct autovec *av_dev; + uchar_t irqno; + int i; + apic_irq_t *irq_p; + + /* Sanity check the vector/irq argument. */ + ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); + + mutex_enter(&airq_mutex); + + /* + * Convert the vecirq arg to an irq using vector_to_irq table + * if the arg is a vector. Pass thru if already an irq. + */ + if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == + PSMGI_INTRBY_VEC) + irqno = apic_vector_to_irq[vecirq]; + else + irqno = vecirq; + + irq_p = apic_irq_table[irqno]; + + if ((irq_p == NULL) || + (irq_p->airq_temp_cpu == IRQ_UNBOUND) || + (irq_p->airq_temp_cpu == IRQ_UNINIT)) { + mutex_exit(&airq_mutex); + return (PSM_FAILURE); + } + + if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { + + /* Get the (temp) cpu from apic_irq table, indexed by irq. */ + intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; + + /* Return user bound info for intrd. */ + if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { + intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; + intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; + } + } + + if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) + intr_params_p->avgi_vector = irq_p->airq_vector; + + if (intr_params_p->avgi_req_flags & + (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) + /* Get number of devices from apic_irq table shared field. */ + intr_params_p->avgi_num_devs = irq_p->airq_share; + + if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { + + intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; + + /* Some devices have NULL dip. Don't count these. */ + if (intr_params_p->avgi_num_devs > 0) { + for (i = 0, av_dev = autovect[irqno].avh_link; + av_dev; av_dev = av_dev->av_link) + if (av_dev->av_vector && av_dev->av_dip) + i++; + intr_params_p->avgi_num_devs = + MIN(intr_params_p->avgi_num_devs, i); + } + + /* There are no viable dips to return. */ + if (intr_params_p->avgi_num_devs == 0) + intr_params_p->avgi_dip_list = NULL; + + else { /* Return list of dips */ + + /* Allocate space in array for that number of devs. */ + intr_params_p->avgi_dip_list = kmem_zalloc( + intr_params_p->avgi_num_devs * + sizeof (dev_info_t *), + KM_SLEEP); + + /* + * Loop through the device list of the autovec table + * filling in the dip array. + * + * Note that the autovect table may have some special + * entries which contain NULL dips. These will be + * ignored. + */ + for (i = 0, av_dev = autovect[irqno].avh_link; + av_dev; av_dev = av_dev->av_link) + if (av_dev->av_vector && av_dev->av_dip) + intr_params_p->avgi_dip_list[i++] = + av_dev->av_dip; + } + } + + mutex_exit(&airq_mutex); + + return (PSM_SUCCESS); +} + + /* * This function provides external interface to the nexus for all * functionalities related to the new DDI interrupt framework. @@ -695,12 +870,11 @@ int apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, psm_intr_op_t intr_op, int *result) { - int cap, ret; + int cap; int count_vec; - int cpu; int old_priority; int new_priority; - int iflag; + int new_cpu; apic_irq_t *irqp; struct intrspec *ispec, intr_spec; @@ -812,42 +986,28 @@ apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, hdlp->ih_pri = new_priority; /* set the new value */ break; case PSM_INTR_OP_SET_CPU: + case PSM_INTR_OP_GRP_SET_CPU: /* * The interrupt handle given here has been allocated * specifically for this command, and ih_private carries * a CPU value. */ - cpu = (int)(intptr_t)hdlp->ih_private; - - if (!apic_cpu_in_range(cpu)) { + new_cpu = (int)(intptr_t)hdlp->ih_private; + if (!apic_cpu_in_range(new_cpu)) { + DDI_INTR_IMPLDBG((CE_CONT, + "[grp_]set_cpu: cpu out of range: %d\n", new_cpu)); *result = EINVAL; return (PSM_FAILURE); } - - - /* Convert the vector to the irq using vector_to_irq table. */ - mutex_enter(&airq_mutex); - irqp = apic_irq_table[apic_vector_to_irq[hdlp->ih_vector]]; - mutex_exit(&airq_mutex); - - if (irqp == NULL) { - *result = ENXIO; - return (PSM_FAILURE); - } - - iflag = intr_clear(); - lock_set(&apic_ioapic_lock); - - ret = apic_rebind_all(irqp, cpu); - - lock_clear(&apic_ioapic_lock); - intr_restore(iflag); - - if (ret) { - *result = EIO; - return (PSM_FAILURE); + if (intr_op == PSM_INTR_OP_SET_CPU) { + if (apic_set_cpu(hdlp->ih_vector, new_cpu, result) != + PSM_SUCCESS) + return (PSM_FAILURE); + } else { + if (apic_grp_set_cpu(hdlp->ih_vector, new_cpu, + result) != PSM_SUCCESS) + return (PSM_FAILURE); } - *result = 0; break; case PSM_INTR_OP_GET_INTR: /* @@ -859,6 +1019,10 @@ apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) return (PSM_FAILURE); break; + case PSM_INTR_OP_APIC_TYPE: + hdlp->ih_private = apic_get_apic_type(); + hdlp->ih_ver = apic_get_apic_version(); + break; case PSM_INTR_OP_SET_CAP: default: return (PSM_FAILURE); diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h index 853be5d7c6..d2022e1347 100644 --- a/usr/src/uts/i86pc/sys/apic.h +++ b/usr/src/uts/i86pc/sys/apic.h @@ -38,6 +38,8 @@ extern "C" { #include <sys/psm_common.h> +#define APIC_PCPLUSMP_NAME "pcplusmp" + #define APIC_IO_ADDR 0xfec00000 #define APIC_LOCAL_ADDR 0xfee00000 #define APIC_IO_MEMLEN 0xf @@ -706,8 +708,6 @@ extern int apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type, int behavior); extern void apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type); -extern int apic_get_vector_intr_info(int vecirq, - apic_get_intr_t *intr_params_p); extern uchar_t apic_find_multi_vectors(int pri, int count); extern int apic_setup_io_intr(void *p, int irq, boolean_t deferred); extern uint32_t *mapin_apic(uint32_t addr, size_t len, int flags); @@ -718,6 +718,8 @@ extern uchar_t apic_modify_vector(uchar_t vector, int irq); extern int apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum); extern int apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum); extern int apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum); +extern char *apic_get_apic_type(); +extern uint16_t apic_get_apic_version(); extern volatile uint32_t *apicadr; /* virtual addr of local APIC */ extern int apic_forceload; diff --git a/usr/src/uts/i86pc/sys/apic_ctlr.h b/usr/src/uts/i86pc/sys/apic_ctlr.h new file mode 100644 index 0000000000..78ecba70b7 --- /dev/null +++ b/usr/src/uts/i86pc/sys/apic_ctlr.h @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_APIC_CTLR_H +#define _SYS_APIC_CTLR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Macros dealing with controller version field returned as part of + * PSM_INTR_OP_APIC_TYPE. + */ +#define PSMAT_LOCAL_APIC_VER(ctlr_ver) ((ctlr_ver) & 0xff) +#define PSMAT_IO_APIC_VER(ctlr_ver) ((ctlr_ver) >> 8) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_APIC_CTLR_H */ diff --git a/usr/src/uts/i86pc/sys/psm_types.h b/usr/src/uts/i86pc/sys/psm_types.h index 435d2da112..70b49ed3dc 100644 --- a/usr/src/uts/i86pc/sys/psm_types.h +++ b/usr/src/uts/i86pc/sys/psm_types.h @@ -58,7 +58,9 @@ typedef enum psm_intr_op_e { PSM_INTR_OP_GET_SHARED, /* 10. Get the shared intr info */ PSM_INTR_OP_CHECK_MSI, /* 11. Chk if device supports MSI */ PSM_INTR_OP_SET_CPU, /* 12. Set vector's CPU */ - PSM_INTR_OP_GET_INTR /* 13. Get vector's info */ + PSM_INTR_OP_GET_INTR, /* 13. Get vector's info */ + PSM_INTR_OP_GRP_SET_CPU, /* 14. Set all device's vectors' CPU */ + PSM_INTR_OP_APIC_TYPE /* 15. Returns APIC type */ } psm_intr_op_t; struct psm_ops { diff --git a/usr/src/uts/sun4/io/px/px_devctl.c b/usr/src/uts/sun4/io/px/px_devctl.c index 731d419b6b..463ab15c3a 100644 --- a/usr/src/uts/sun4/io/px/px_devctl.c +++ b/usr/src/uts/sun4/io/px/px_devctl.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -240,7 +240,7 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) /*FALLTHRU*/ /* These require no special privileges. */ case PCITOOL_DEVICE_GET_INTR: - case PCITOOL_DEVICE_NUM_INTR: + case PCITOOL_SYSTEM_INTR_INFO: rv = pxtool_intr(dip, (void *)arg, cmd, mode); break; diff --git a/usr/src/uts/sun4/io/px/px_intr.c b/usr/src/uts/sun4/io/px/px_intr.c index e9ad60378c..cb29b53996 100644 --- a/usr/src/uts/sun4/io/px/px_intr.c +++ b/usr/src/uts/sun4/io/px/px_intr.c @@ -346,7 +346,8 @@ px_msiq_intr(caddr_t arg) ih_p && (j < ipil_p->ipil_ih_size) && ((ih_p->ih_msg_code != msg_code) || (ih_p->ih_rec_type != msiq_rec_p->msiq_rec_type)); - ih_p = ih_p->ih_next, j++); + ih_p = ih_p->ih_next, j++) + ; if ((ih_p->ih_msg_code == msg_code) && (ih_p->ih_rec_type == msiq_rec_p->msiq_rec_type)) { @@ -996,8 +997,21 @@ px_ks_update(kstat_t *ksp, int rw) if (ih_p->ih_intr_state == PX_INTR_STATE_ENABLE) { - (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, - (ih_p->ih_rec_type == 0) ? "fixed" : "msi"); + switch (i_ddi_intr_get_current_type(ih_p->ih_dip)) { + case DDI_INTR_TYPE_MSI: + (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, + "msi"); + break; + case DDI_INTR_TYPE_MSIX: + (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, + "msix"); + break; + default: + (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, + "fixed"); + break; + } + pxintr_ks_template.pxintr_ks_cpu.value.ui64 = ino_p->ino_cpuid; pxintr_ks_template.pxintr_ks_pil.value.ui64 = ipil_p->ipil_pil; pxintr_ks_template.pxintr_ks_time.value.ui64 = ih_p->ih_nsec + diff --git a/usr/src/uts/sun4/io/px/px_tools.c b/usr/src/uts/sun4/io/px/px_tools.c index 5bce819abf..bafc8b8f79 100644 --- a/usr/src/uts/sun4/io/px/px_tools.c +++ b/usr/src/uts/sun4/io/px/px_tools.c @@ -84,15 +84,33 @@ pxtool_validate_cpuid(uint32_t cpuid) } +/*ARGSUSED*/ static int -pxtool_intr_get_max_ino(uint32_t *arg, int mode) +pxtool_intr_info(dev_info_t *dip, void *arg, int mode) { - if (ddi_copyout(&pxtool_num_inos, arg, sizeof (uint32_t), mode) != - DDI_SUCCESS) + pcitool_intr_info_t intr_info; + int rval = SUCCESS; + + /* If we need user_version, and to ret same user version as passed in */ + if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != + DDI_SUCCESS) { return (EFAULT); - else - return (SUCCESS); + } + + intr_info.ctlr_version = 0; /* XXX how to get real version? */ + intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; + intr_info.num_intr = pxtool_num_inos; + + intr_info.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != + DDI_SUCCESS) { + rval = EFAULT; + } + + return (rval); } + + /* * Get interrupt information for a given ino. * Returns info only for inos mapped to devices. @@ -203,6 +221,7 @@ pxtool_get_intr(dev_info_t *dip, void *arg, int mode) rval = SUCCESS; done_get_intr: + iget->drvr_version = PCITOOL_VERSION; copyout_rval = ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); @@ -231,11 +250,38 @@ pxtool_set_intr(dev_info_t *dip, void *arg, int mode) px_ib_t *ib_p = px_p->px_ib_p; uint8_t zero = 0; int rval = SUCCESS; + size_t copyinout_size; - if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + bzero(&iset, sizeof (pcitool_intr_set_t)); + + /* Version 1 of pcitool_intr_set_t doesn't have flags. */ + copyinout_size = (size_t)&iset.flags - (size_t)&iset; + + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) return (EFAULT); + switch (iset.user_version) { + case PCITOOL_V1: + break; + + case PCITOOL_V2: + copyinout_size = sizeof (pcitool_intr_set_t); + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) + return (EFAULT); + break; + + default: + iset.status = PCITOOL_OUT_OF_RANGE; + rval = ENOTSUP; + goto done_set_intr; + } + + if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) { + iset.status = PCITOOL_IO_ERROR; + rval = ENOTSUP; + goto done_set_intr; + } + iset.status = PCITOOL_INVALID_INO; rval = EINVAL; @@ -283,8 +329,8 @@ pxtool_set_intr(dev_info_t *dip, void *arg, int mode) mutex_exit(&cpu_lock); done_set_intr: - if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + iset.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) rval = EFAULT; return (rval); @@ -299,9 +345,9 @@ pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode) switch (cmd) { - /* Return the number of interrupts supported by a PCI bus. */ - case PCITOOL_DEVICE_NUM_INTR: - rval = pxtool_intr_get_max_ino(arg, mode); + /* Get system interrupt information. */ + case PCITOOL_SYSTEM_INTR_INFO: + rval = pxtool_intr_info(dip, arg, mode); break; /* Get interrupt information for a given ino. */ @@ -655,6 +701,7 @@ pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag); done_reg: + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DBG(DBG_TOOLS, dip, "Error returning arguments.\n"); diff --git a/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c b/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c index 4899c83369..850501397a 100644 --- a/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c +++ b/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -154,7 +154,7 @@ fpc_platform_node_init(dev_info_t *dip, int *avail) nodename = kmem_zalloc(nodename_size, KM_SLEEP); platform_specific_data = - kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP); + kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP); (void) strcpy(nodename, name); (void) strcat(nodename, ":"); @@ -163,7 +163,7 @@ fpc_platform_node_init(dev_info_t *dip, int *avail) /* Get register banks. */ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) { + "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) { goto bad_regs_p; } @@ -262,7 +262,7 @@ fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, (fire_counter_handle_impl_t *)handle; int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; - prg.user_version = PCITOOL_USER_VERSION; + prg.user_version = PCITOOL_VERSION; if (group == jbc) { prg.barnum = JBUS_BANK; @@ -303,7 +303,7 @@ fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, int command = (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; - prg.user_version = PCITOOL_USER_VERSION; + prg.user_version = PCITOOL_VERSION; /* * Note that stated PCIE offsets are relative to the beginning of their * register bank, while JBUS offsets are absolute. diff --git a/usr/src/uts/sun4u/io/pci/pci_devctl.c b/usr/src/uts/sun4u/io/pci/pci_devctl.c index e317de98db..4bd413f06f 100644 --- a/usr/src/uts/sun4u/io/pci/pci_devctl.c +++ b/usr/src/uts/sun4u/io/pci/pci_devctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -301,7 +300,7 @@ pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) /*FALLTHRU*/ /* These require no special privileges. */ case PCITOOL_DEVICE_GET_INTR: - case PCITOOL_DEVICE_NUM_INTR: + case PCITOOL_SYSTEM_INTR_INFO: rv = pcitool_intr_admn(dev, (void *)arg, cmd, mode); break; } diff --git a/usr/src/uts/sun4u/io/pci/pci_tools.c b/usr/src/uts/sun4u/io/pci/pci_tools.c index 2260447087..0e376c9dcd 100644 --- a/usr/src/uts/sun4u/io/pci/pci_tools.c +++ b/usr/src/uts/sun4u/io/pci/pci_tools.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -268,18 +267,30 @@ pcitool_validate_cpuid(uint32_t cpuid) } -/* Return the number of interrupts on a pci bus. */ +/*ARGSUSED*/ static int -pcitool_intr_get_max_ino(uint32_t *arg, int mode) +pcitool_intr_info(dev_info_t *dip, void *arg, int mode) { - uint32_t num_intr = PCI_MAX_INO; + pcitool_intr_info_t intr_info; + int rval = SUCCESS; - if (ddi_copyout(&num_intr, arg, sizeof (uint32_t), mode) != + /* If we need user_version, and to ret same user version as passed in */ + if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) != DDI_SUCCESS) { return (EFAULT); - } else { - return (SUCCESS); } + + intr_info.ctlr_version = 0; /* XXX how to get real version? */ + intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC; + intr_info.num_intr = PCI_MAX_INO; + + intr_info.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) != + DDI_SUCCESS) { + rval = EFAULT; + } + + return (rval); } @@ -381,7 +392,7 @@ pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) } } done_get_intr: - iget->drvr_version = PCITOOL_DRVR_VERSION; + iget->drvr_version = PCITOOL_VERSION; copyout_rval = ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(num_devs_ret), mode); @@ -415,11 +426,38 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) uint64_t new_imregval; volatile uint64_t *imregp; volatile uint64_t *idregp; + size_t copyinout_size; + + bzero(&iset, sizeof (pcitool_intr_set_t)); + + /* Version 1 of pcitool_intr_set_t doesn't have flags. */ + copyinout_size = (size_t)&iset.flags - (size_t)&iset; - if (ddi_copyin(arg, &iset, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) return (EFAULT); + switch (iset.user_version) { + case PCITOOL_V1: + break; + + case PCITOOL_V2: + copyinout_size = sizeof (pcitool_intr_set_t); + if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS) + return (EFAULT); + break; + + default: + iset.status = PCITOOL_OUT_OF_RANGE; + rval = ENOTSUP; + goto done_set_intr; + } + + if (iset.flags & PCITOOL_INTR_SET_FLAG_GROUP) { + iset.status = PCITOOL_IO_ERROR; + rval = ENOTSUP; + goto done_set_intr; + } + /* Validate input argument and that ino given belongs to a device. */ if ((iset.ino > PCI_MAX_INO) || (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) { @@ -514,9 +552,8 @@ pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p) rval = EINVAL; } done_set_intr: - iset.drvr_version = PCITOOL_DRVR_VERSION; - if (ddi_copyout(&iset, arg, sizeof (pcitool_intr_set_t), mode) != - DDI_SUCCESS) + iset.drvr_version = PCITOOL_VERSION; + if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS) rval = EFAULT; return (rval); @@ -533,9 +570,9 @@ pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode) switch (cmd) { - /* Return the number of interrupts supported by a PCI bus. */ - case PCITOOL_DEVICE_NUM_INTR: - rval = pcitool_intr_get_max_ino(arg, mode); + /* Get system interrupt information. */ + case PCITOOL_SYSTEM_INTR_INFO: + rval = pcitool_intr_info(dip, arg, mode); break; /* Get interrupt information for a given ino. */ @@ -701,7 +738,7 @@ done: if (pci_rp != NULL) ddi_prop_free(pci_rp); - prg.drvr_version = PCITOOL_DRVR_VERSION; + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n"); @@ -989,7 +1026,7 @@ pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode) } done_reg: - prg.drvr_version = PCITOOL_DRVR_VERSION; + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n"); diff --git a/usr/src/uts/sun4u/io/px/px_tools_4u.c b/usr/src/uts/sun4u/io/px/px_tools_4u.c index 7b9c459bd3..f1d0cb1b2e 100644 --- a/usr/src/uts/sun4u/io/px/px_tools_4u.c +++ b/usr/src/uts/sun4u/io/px/px_tools_4u.c @@ -403,6 +403,7 @@ done: if (px_rp != NULL) ddi_prop_free(px_rp); + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DBG(DBG_TOOLS, dip, "Copyout failed.\n"); diff --git a/usr/src/uts/sun4v/io/px/px_tools_4v.c b/usr/src/uts/sun4v/io/px/px_tools_4v.c index a13027ad57..cf0c31c044 100644 --- a/usr/src/uts/sun4v/io/px/px_tools_4v.c +++ b/usr/src/uts/sun4v/io/px/px_tools_4v.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -516,6 +516,7 @@ pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data, PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write); done: + prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DBG(DBG_TOOLS, dip, "Copyout failed.\n"); |