Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net

Cross-merge networking fixes after downstream PR (net-7.3-rc2).

No conflicts, or adjacent changes.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-06 11:51:42 -07:00
commit a5249d1d9a
572 changed files with 12782 additions and 6360 deletions

View File

@ -242,6 +242,10 @@ Daniele Alessandrelli <daniele.alessandrelli@gmail.com> <daniele.alessandrelli@i
Danila Tikhonov <danila@mainlining.org> <danila@jiaxyga.com>
Danila Tikhonov <danila@mainlining.org> <JIaxyga@protonmail.com>
Danilo Krummrich <dakr@kernel.org> <dakr@redhat.com>
Darrick J. Wong <djwong@kernel.org> <djwong@us.ibm.com>
Darrick J. Wong <djwong@kernel.org> <darrick.wong@oracle.com>
Darrick J. Wong <djwong@kernel.org> <djwong@flax.djwong.org>
Darrick J. Wong <djwong@kernel.org> <djwong@djwong.org>
David Brownell <david-b@pacbell.net>
David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org>
David Gow <david@davidgow.net> <davidgow@google.com>
@ -577,6 +581,9 @@ Mark Brown <broonie@sirena.org.uk>
Mark Starovoytov <mstarovo@pm.me> <mstarovoitov@marvell.com>
Markus Schneider-Pargmann <msp@baylibre.com> <mpa@pengutronix.de>
Mark Yao <markyao0591@gmail.com> <mark.yao@rock-chips.com>
Martin K. Petersen <mkp@kernel.org> <martin.petersen@oracle.com>
Martin K. Petersen <mkp@kernel.org> <mkp@mkp.net>
Martin K. Petersen <mkp@kernel.org> <mkp@linuxcare.com>
Martin Kepplinger-Novakovic <martink@posteo.de> <martin.kepplinger-novakovic@ginzinger.com>
Martyna Szapar-Mudlaw <martyna.szapar-mudlaw@linux.intel.com> <martyna.szapar-mudlaw@intel.com>
Mathieu Othacehe <othacehe@gnu.org> <m.othacehe@gmail.com>

11
CREDITS
View File

@ -1512,6 +1512,10 @@ N: Andy Gross
E: agross@kernel.org
D: Qualcomm SoC subsystem and drivers
N: Mark Gross
E: markgross@kernel.org
D: x86/mellanox platform maintenance and various x86 specific drivers
N: Grant Grundler
E: grantgrundler@gmail.com
W: http://obmouse.sourceforge.net/
@ -4469,6 +4473,13 @@ S: The Netherlands
N: Thomas Woller
D: CS461x Cirrus Logic sound driver
N: Darrick J. Wong
E: djwong@kernel.org
D: ext4 filesystem
D: XFS filesystem and online fsck
D: Various hwmon drivers
S: USA
N: David Woodhouse
E: dwmw2@infradead.org
D: JFFS2 file system, Memory Technology Device subsystem,

View File

@ -986,6 +986,7 @@ Description: This sysfs entry can be used to enable/disable to adjust priority f
0x00000008 gc_lock
0x00000010 cp_global
0x00000020 io_rwsem
0x00000040 nat_tree_lock
========== ==================
What: /sys/fs/f2fs/<disk>/lock_duration_priority
@ -1002,3 +1003,20 @@ Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ck
threads, limitation as below:
- it requires user has CAP_SYS_NICE capability.
- the range is [100, 139], by default the value is 120.
What: /sys/fs/f2fs/<disk>/max_atc_write_bio_size
Date: June 2026
Contact: Bart Van Assche <bvanassche@acm.org>
Description: Every time a write operation completes f2fs_write_end_io() is
called. This function may be called from an atomic context,
e.g. from inside an interrupt handler. This attribute controls
the maximum size of a write bio that is completed in atomic
(atc) context. The default value for this attribute is UINT_MAX
which means that this functionality is disabled by default.
What: /sys/fs/f2fs/<disk>/pinned_area_max_secno
Date: August 2026
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: This is a read-only entry to show the upper bound section number
for pinned files. Pinned files will only be allocated within
sections 0 to pinned_area_max_secno - 1.

View File

@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle
processes can be categorized as follows:
* Processes under the fair-class scheduler
* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback
* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback
* Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler
without the ``cgroup_set_weight`` callback
without the corresponding ``cgroup_set_*`` callback
For details on when a process is under the fair-class scheduler or a BPF scheduler,
check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`.
@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds.
$PERIOD duration. "max" for $MAX indicates no limit. If only
one number is written, $MAX is updated.
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
the callback actually does.
cpu.max.burst
A read-write single value file which exists on non-root
@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds.
The burst in the range [0, $MAX].
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
the callback actually does.
cpu.pressure
A read-write nested-keyed file.
@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds.
own relative priorities, but the cgroup itself will be treated as
very low priority relative to its peers.
This file affects only processes under the fair-class scheduler.
This file affects only processes under the fair-class scheduler and a BPF
scheduler with the ``cgroup_set_idle`` callback depending on what the
callback actually does.
Memory
------
@ -3064,7 +3070,7 @@ resources (res_a and res_b) are registered then:
change in this file generates a file modified event. All fields in
this file are hierarchical.
max
<res>.max
The number of times the cgroup's resource usage was
about to go over the max boundary.

View File

@ -47,6 +47,10 @@ properties:
- qcom,sm8650-dsi-ctrl
- qcom,sm8750-dsi-ctrl
- const: qcom,mdss-dsi-ctrl
- items:
- const: qcom,shikra-dsi-ctrl
- const: qcom,qcm2290-dsi-ctrl
- const: qcom,mdss-dsi-ctrl
- items:
- enum:
- qcom,qcs8300-dsi-ctrl

View File

@ -262,6 +262,7 @@ allOf:
compatible:
contains:
enum:
- qcom,adreno-gmu-722.0
- qcom,adreno-gmu-730.1
- qcom,adreno-gmu-740.1
- qcom,adreno-gmu-750.1

View File

@ -353,6 +353,7 @@ allOf:
- qcom,adreno-610.0
- qcom,adreno-619.1
- qcom,adreno-07000200
- qcom,adreno-07000400
then:
properties:
clocks:
@ -413,6 +414,7 @@ allOf:
contains:
enum:
- qcom,adreno-44010000
- qcom,adreno-44050a01
- qcom,adreno-44070001
then:
properties:
@ -443,11 +445,13 @@ allOf:
- qcom,adreno-680.1
- qcom,adreno-690.0
- qcom,adreno-730.1
- qcom,adreno-43020100
- qcom,adreno-43030c00
- qcom,adreno-43050a01
- qcom,adreno-43050c01
- qcom,adreno-43051401
- qcom,adreno-44010000
- qcom,adreno-44050a01
- qcom,adreno-44070001
then: # Starting with A6xx, the clocks are usually defined in the GMU node

View File

@ -13,7 +13,11 @@ $ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,qcm2290-dpu
oneOf:
- const: qcom,qcm2290-dpu
- items:
- const: qcom,shikra-dpu
- const: qcom,qcm2290-dpu
reg:
items:

View File

@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/display/msm/qcom,qcm2290-mdss.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm QCM220 Display MDSS
title: Qualcomm QCM2290 and Shikra Display MDSS
maintainers:
- Loic Poulain <loic.poulain@linaro.org>
@ -12,13 +12,18 @@ maintainers:
description:
Device tree bindings for MSM Mobile Display Subsystem(MDSS) that encapsulates
sub-blocks like DPU display controller and DSI. Device tree bindings of MDSS
are mentioned for QCM2290 target.
are mentioned for QCM2290 and Shikra targets. Shikra uses the same MDSS/DPU/DSI
hardware as QCM2290 (DPU 6.5) and shares the same register layout.
$ref: /schemas/display/msm/mdss-common.yaml#
properties:
compatible:
const: qcom,qcm2290-mdss
oneOf:
- const: qcom,qcm2290-mdss
- items:
- const: qcom,shikra-mdss
- const: qcom,qcm2290-mdss
clocks:
items:
@ -52,7 +57,8 @@ patternProperties:
properties:
compatible:
const: qcom,qcm2290-dpu
contains:
const: qcom,qcm2290-dpu
"^dsi@[0-9a-f]+$":
type: object
@ -60,9 +66,8 @@ patternProperties:
properties:
compatible:
items:
- const: qcom,qcm2290-dsi-ctrl
- const: qcom,mdss-dsi-ctrl
contains:
const: qcom,qcm2290-dsi-ctrl
"^phy@[0-9a-f]+$":
type: object

View File

@ -1,21 +0,0 @@
* Microchip PIC32 Real Time Clock and Calendar
The RTCC keeps time in hours, minutes, and seconds, and one half second. It
provides a calendar in weekday, date, month, and year. It also provides a
configurable alarm.
Required properties:
- compatible: should be: "microchip,pic32mzda-rtc"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: RTC alarm/event interrupt
- clocks: clock phandle
Example:
rtc: rtc@1f8c0000 {
compatible = "microchip,pic32mzda-rtc";
reg = <0x1f8c0000 0x60>;
interrupts = <166 IRQ_TYPE_EDGE_RISING>;
clocks = <&PBCLK6>;
};

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/microchip,pic32mzda-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PIC32 Real Time Clock and Calendar
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
description: |
The Microchip PIC32 Real Time Clock and Calendar (RTCC) keeps time in hours,
minutes, seconds, and one half second. It also provides a calendar with
weekday, date, month, and year, along with a configurable alarm.
allOf:
- $ref: rtc.yaml#
properties:
compatible:
const: microchip,pic32mzda-rtc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
rtc@1f8c0000 {
compatible = "microchip,pic32mzda-rtc";
reg = <0x1f8c0000 0x60>;
interrupts = <166 IRQ_TYPE_EDGE_RISING>;
clocks = <&PBCLK6>;
};

View File

@ -9,15 +9,19 @@ title: Renesas RZ/N1 SoCs Real-Time Clock
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
allOf:
- $ref: rtc.yaml#
properties:
compatible:
items:
- enum:
- renesas,r9a06g032-rtc
- const: renesas,rzn1-rtc
oneOf:
- items:
- enum:
- renesas,r9a06g032-rtc
- const: renesas,rzn1-rtc
- const: renesas,r9a09g077-rtc
- items:
- const: renesas,r9a09g087-rtc
- const: renesas,r9a09g077-rtc
reg:
maxItems: 1
@ -54,6 +58,23 @@ required:
- clock-names
- power-domains
allOf:
- $ref: rtc.yaml#
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g077-rtc
- renesas,r9a09g087-rtc
then:
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
unevaluatedProperties: false
examples:

View File

@ -1,27 +0,0 @@
Motorola mc146818 compatible RTC
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Required properties:
- compatible : "motorola,mc146818"
- reg : should contain registers location and length.
Optional properties:
- interrupts : should contain interrupt.
- ctrl-reg : Contains the initial value of the control register also
called "Register B".
- freq-reg : Contains the initial value of the frequency register also
called "Register A".
"Register A" and "B" are usually initialized by the firmware (BIOS for
instance). If this is not done, it can be performed by the driver.
ISA Example:
rtc@70 {
compatible = "motorola,mc146818";
interrupts = <8 3>;
interrupt-parent = <&ioapic1>;
ctrl-reg = <2>;
freq-reg = <0x26>;
reg = <1 0x70 2>;
};

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/rtc-cmos.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Motorola mc146818 compatible RTC
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
properties:
compatible:
oneOf:
- const: motorola,mc146818
- items:
- const: intel,ce4100-rtc
- const: motorola,mc146818
reg:
maxItems: 1
interrupts:
maxItems: 1
ctrl-reg:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Initial value of the control register
(also known as Register B).
freq-reg:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Initial value of the frequency register
(also known as Register A).
required:
- compatible
- reg
allOf:
- $ref: rtc.yaml#
unevaluatedProperties: false
examples:
- |
bus {
#address-cells = <2>;
#size-cells = <1>;
rtc@1,70 {
compatible = "motorola,mc146818";
reg = <0x1 0x70 0x2>;
interrupts = <8 3>;
ctrl-reg = <2>;
freq-reg = <0x26>;
};
};

View File

@ -1,53 +0,0 @@
TI Real Time Clock
Required properties:
- compatible:
- "ti,da830-rtc" - for RTC IP used similar to that on DA8xx SoC family.
- "ti,am3352-rtc" - for RTC IP used similar to that on AM335x SoC family.
This RTC IP has special WAKE-EN Register to enable
Wakeup generation for event Alarm. It can also be
used to control an external PMIC via the
pmic_power_en pin.
- "ti,am4372-rtc" - for RTC IP used similar to that on AM437X SoC family.
- reg: Address range of rtc register set
- interrupts: rtc timer, alarm interrupts in order
Optional properties:
- system-power-controller: whether the rtc is controlling the system power
through pmic_power_en
- clocks: Any internal or external clocks feeding in to rtc
- clock-names: Corresponding names of the clocks
- pinctrl-0: a phandle pointing to the pin settings for the device
- pinctrl-names: should be "default"
Optional subnodes:
- generic pinctrl node
Required pinctrl subnodes properties:
- pins - Names of ext_wakeup pins to configure
Optional pinctrl subnodes properties:
- input-enable - Enables ext_wakeup
- ti,active-high - Set input active high (by default active low)
Example:
rtc@1c23000 {
compatible = "ti,da830-rtc";
reg = <0x23000 0x1000>;
interrupts = <19
19>;
interrupt-parent = <&intc>;
system-power-controller;
clocks = <&clk_32k_rtc>, <&clk_32768_ck>;
clock-names = "ext-clk", "int-clk";
pinctrl-0 = <&ext_wakeup>;
pinctrl-names = "default";
ext_wakeup: ext-wakeup {
pins = "ext_wakeup0";
input-enable;
ti,active-high;
};
};

View File

@ -1,32 +0,0 @@
Palmas RTC controller bindings
Required properties:
- compatible:
- "ti,palmas-rtc" for palma series of the RTC controller
- interrupts: Interrupt number of RTC submodule on device.
Optional properties:
- ti,backup-battery-chargeable: The Palmas series device like TPS65913 or
TPS80036 supports the backup battery for powering the RTC when main
battery is removed or in very low power state. The backup battery
can be chargeable or non-chargeable. This flag will tells whether
battery is chargeable or not. If charging battery then driver can
enable the charging.
- ti,backup-battery-charge-high-current: Enable high current charging in
backup battery. Device supports the < 100uA and > 100uA charging.
The high current will be > 100uA. Absence of this property will
charge battery to lower current i.e. < 100uA.
Example:
palmas: tps65913@58 {
...
palmas_rtc: rtc {
compatible = "ti,palmas-rtc";
interrupt-parent = <&palmas>;
interrupts = <8 0>;
ti,backup-battery-chargeable;
ti,backup-battery-charge-high-current;
};
...
};

View File

@ -0,0 +1,140 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/ti,omap-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments OMAP Real Time Clock
maintainers:
- Keerthy <j-keerthy@ti.com>
- Afzal Mohammed <afzal@ti.com>
description:
The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock with
century-range alarm matching, driven by the 32kHz clock.
properties:
compatible:
oneOf:
- const: ti,da830-rtc
- items:
- const: ti,am3352-rtc
- const: ti,da830-rtc
- items:
- const: ti,am4372-rtc
- const: ti,am3352-rtc
- const: ti,da830-rtc
reg:
maxItems: 1
interrupts:
minItems: 2
maxItems: 2
system-power-controller:
type: boolean
description:
Indicates that this RTC controls system power via the pmic_power_en pin.
clocks:
minItems: 1
maxItems: 2
clock-names:
minItems: 1
items:
- enum: [ext-clk, int-clk]
- const: int-clk
pinctrl-0:
description:
Phandle to pin configuration for the external wakeup pins.
pinctrl-names:
minItems: 1
ti,hwmods:
$ref: /schemas/types.yaml#/definitions/string
description:
Name of the hwmod associated with the RTC.
const: rtc
deprecated: true
patternProperties:
"^ext-wakeup(-[0-9]+)?$":
type: object
description:
Child node describing external wakeup pin configuration.
properties:
pins:
pattern: '^ext_wakeup[0-3]$'
input-enable:
type: boolean
description:
Enables the external wakeup input on the selected pin.
ti,active-high:
type: boolean
description:
Sets the wakeup input polarity to active high. By default the
input is active low.
required:
- pins
additionalProperties: false
required:
- compatible
- reg
- interrupts
allOf:
- $ref: rtc.yaml#
- if:
not:
properties:
compatible:
contains:
const: ti,am3352-rtc
then:
properties:
system-power-controller: false
patternProperties:
"^ext-wakeup(-[0-9]+)?$": false
unevaluatedProperties: false
examples:
- |
rtc@23000 {
compatible = "ti,da830-rtc";
reg = <0x23000 0x1000>;
interrupts = <19>, <19>;
clocks = <&clk_32768_ck>;
clock-names = "int-clk";
};
- |
rtc@0 {
compatible = "ti,am3352-rtc", "ti,da830-rtc";
reg = <0x0 0x1000>;
interrupts = <75>, <76>;
system-power-controller;
clocks = <&clk_32k_rtc>, <&clk_32768_ck>;
clock-names = "ext-clk", "int-clk";
pinctrl-0 = <&ext_wakeup>;
pinctrl-names = "default";
ext-wakeup {
pins = "ext_wakeup0";
input-enable;
ti,active-high;
};
};

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/ti,palmas-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments Palmas RTC
maintainers:
- Eduard Bostina <egbostina@gmail.com>
allOf:
- $ref: /schemas/rtc/rtc.yaml#
properties:
compatible:
const: ti,palmas-rtc
interrupts:
maxItems: 1
wakeup-source: true
ti,backup-battery-chargeable:
type: boolean
description:
The backup battery can be chargeable or non-chargeable. This flag
indicates whether the battery is chargeable. If present, the driver
can enable charging.
ti,backup-battery-charge-high-current:
type: boolean
description:
Enable high current charging in the backup battery.
Device supports the < 100uA and > 100uA charging. The high current will
be > 100uA. Absence of this property will charge battery to lower
current i.e. < 100uA.
required:
- compatible
- interrupts
unevaluatedProperties: false
examples:
- |
pmic {
#address-cells = <1>;
#size-cells = <0>;
rtc {
compatible = "ti,palmas-rtc";
interrupts = <8 0>;
ti,backup-battery-chargeable;
ti,backup-battery-charge-high-current;
};
};

View File

@ -194,6 +194,12 @@ Mount Options
copies. Currently, it's only used in copy_file_range, which will revert
to the default VFS implementation if this option is used.
nearfull_sync
Force written data to stable storage when the cluster or file data pool is
marked NEARFULL. This restores the legacy client-side backpressure
behavior. By default, CephFS writes are not forced synchronous solely
because of NEARFULL.
recover_session=<no|clean>
Set auto reconnect mode in the case where the client is blocklisted. The
available modes are "no" and "clean". The default is "no".

View File

@ -415,7 +415,13 @@ lookup_mode=%s Control the directory lookup behavior for casefolded
auto F2FS determines the mode based on the
on-disk `SB_ENC_NO_COMPAT_FALLBACK_FL`
flag.
================== ========================================
resizable_tail_secno=%u Control the number of sections at the tail of the
filesystem reserved for online resizing. Pinned files
will only be allocated within sections 0 to
(MAIN_SECS - resizable_tail_secno) - 1. If set to 0
(default), there is no tail restriction unless running
on a zoned block device where conventional zones are
used.
======================== ============================================================
Debugfs Entries
@ -1043,6 +1049,41 @@ So, the key idea is, user can do any file operations on /dev/vdc, and
reclaim the space after the use, while the space is counted as /data.
That doesn't require modifying partition size and filesystem format.
Dynamic Device Aliasing Management
----------------------------------
In addition to static device aliasing by deleting the aliasing file, F2FS
supports dynamic management of device aliasing. This mechanism allows the system
to dynamically transition partition ownership between F2FS userdata and external
entities (e.g., zRAM, raw partition) based on system requirements without
deleting the master aliasing file or requiring unmount/remount.
The master aliasing file is created during the initial format of the file system
and remains as a persistent control entity (ioctl gateway) in the root directory.
- Partition Reservation (In-service to Aliased)
When a specific partition needs to be dedicated to external services (e.g., zRAM),
a user can reserve the device alias range via ioctl. The kernel resets GC victim
information for the target range, marks segments as in-use to prevent new
allocations, and triggers forced GC to migrate existing valid data out of the
range. Finally, it reserves these blocks in the SIT to effectively exclude the
device from the usable capacity.
- Partition Release (Aliased to In-service)
When external usage concludes, the space is reclaimed not by deleting the file,
but through the release ioctl. The kernel truncates blocks associated with
the file, releasing them back to general filesystem allocation.
.. code-block::
# f2fs_io dev_alias release /mnt/f2fs/vdc.file
# df -h
/dev/vdb 64G 753M 64G 2% /mnt/f2fs
# f2fs_io dev_alias reserve /mnt/f2fs/vdc.file
# df -h
/dev/vdb 64G 33G 32G 52% /mnt/f2fs
Per-file Read-Only Large Folio Support
--------------------------------------

View File

@ -360,6 +360,17 @@ attribute-sets:
name: tsoff
type: u32
byte-order: big-endian
-
name: filter-attrs
attributes:
-
name: orig-flags
type: u32
doc: bitmask of tuple fields to filter on, original direction
-
name: reply-flags
type: u32
doc: bitmask of tuple fields to filter on, reply direction
-
name: conntrack-attrs
attributes:
@ -466,7 +477,7 @@ attribute-sets:
-
name: filter
type: nest
nested-attributes: tuple-attrs
nested-attributes: filter-attrs
-
name: status-mask
type: u32
@ -591,10 +602,14 @@ operations:
request:
value: 0x101
attributes:
- mark
- filter
- tuple-orig
- tuple-reply
- status
- mark
- zone
- mark-mask
- filter
- status-mask
reply:
value: 0x100
attributes:

View File

@ -312,7 +312,8 @@ be found at https://sparse.wiki.kernel.org/index.php/Main_Page if your
distributor does not package it); it can then be run on the code by adding
"C=1" to your make command.
The "Coccinelle" tool (http://coccinelle.lip6.fr/) is able to find a wide
The "Coccinelle" tool
(https://coccinelle.gitlabpages.inria.fr/website) is able to find a wide
variety of potential coding problems; it can also propose fixes for those
problems. Quite a few "semantic patches" for the kernel have been packaged
under the scripts/coccinelle directory; running "make coccicheck" will run

View File

@ -230,7 +230,7 @@ optional. The following modified excerpt is from
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
{
exit_type = ei->type;
exit_type = ei->kind;
}
SEC(".struct_ops")
@ -242,6 +242,21 @@ optional. The following modified excerpt is from
.name = "simple",
};
Scheduler-Dependent Knobs
-------------------------
The fair-class scheduler enforces CPU controller settings such as
``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the
scheduler core communicates these settings to the BPF scheduler
through ``ops.cgroup_init()`` and reports subsequent changes through
the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task
nice changes are converted to weights and reported through
``ops.set_weight()``.
Each BPF scheduler is responsible for implementing the scheduling
semantics of these settings and may choose to ignore them. Consult the
loaded scheduler's documentation before relying on these controls.
Dispatch Queues
---------------

View File

@ -22,6 +22,7 @@ Supported devices
Features
========
- PCIe Gen4 x8 host interface
- Support for SAS and SATA devices
- RAID levels: 0, 1, 10, 5, 50, 6, 60
@ -50,16 +51,20 @@ LeapRAID specific disk attributes
::
/sys/class/scsi_disk/host:bus:target:lun/device/sas_device_handle
/sys/class/scsi_disk/host:bus:target:lun/device/ncq_cmd_prio_enable
/sys/block/<disk>/device/sas_device_handle
/sys/block/<disk>/device/sas_ncq_prio_supported
/sys/block/<disk>/device/sas_ncq_prio_enable
The read-only attribute "sas_device_handle" represents the disk's device
handle, which is a unique identifier maintained by the firmware.
This attribute "ncq_cmd_prio_enable" controls NCQ command priority. A value
The read-only attribute "sas_ncq_prio_supported" reports whether a SATA
device supports NCQ command priority.
The attribute "sas_ncq_prio_enable" controls NCQ command priority. A value
of 0 disables NCQ priority handling for RT-priority I/O. Writing 1 enables
NCQ priority handling when the device reports support for the feature through
VPD page 0x89. Unsupported devices keep the effective state at 0.
VPD page 0x89. Writes to unsupported devices fail with an error.
LeapRAID module parameters
==========================
@ -100,6 +105,7 @@ in io_uring poll mode. The default value is 0.
File Location
=============
The driver source is located at:
``drivers/scsi/leapraid/``

View File

@ -2316,6 +2316,9 @@ quirk_flags
applies the ``mixer_playback_min_mute`` flag and clears the
``ignore_ctl_error`` flag for the device 1234:abcd, and applies the
``skip_sample_rate`` flag for all devices.
New quirk flags may replace old ones by reusing the latter's bits, so the
new usage is preferred. Despite that, depending on the order of probing is
fragile, so it'd better migrate to the new usage anyway.
* bit 0: ``get_sample_rate``
Skip reading sample rate for devices
@ -2389,23 +2392,21 @@ quirk_flags
from snd_usb_handle_sync_urb. Instead fall through and enqueue a
packet_info containing only size-0 packets, so the OUT ring keeps
moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
* bit 30: ``mixer_get_cur_broken``
Some mixers are sticky, which means that setting their current volume
is a no-op, and reading the current volume returns a constant value.
The sticky check disables these mixers to prevent confusing userspace.
However, some devices do have a tunable volume despite the reported
current volume being constant. As the sticky check can't distinguish
between the two categories, setting this flag tells that the device
should fall into the second category when GET_CUR returns a constant
value, resulting in the sticky check being non-fatal and only
disabling GET_CUR instead of the whole mixer. The current volume will
then be provided by the internal cache that stores the last set
volume
* bit 30: ``mixer_get_cur_ok``
On some devices, whether their GET_CUR being sticky depends on whether
hotpluggable components are present. When the hotpluggable components
are missing on probe, their GET_CUR behavior is classified as broken.
Set the flag to prevent the heuristics from gating GET_CUR.
* bit 31: ``playback_urb_fixup``
Some devices show the stuttering at playback, and this quirk
works around it by enforcing the fixed max URBs (12) instead of
the dynamic calculation from the buffer size, and passing the
`URB_ISO_ASAP` URB flag.
* bit 32: ``always_set_rate``
Issue SET_CUR for the sample rate even when the clock already reports
the requested rate. A device advertising a single rate is otherwise
never sent the request at all, and some require it before streaming
will start.
This module supports multiple devices, autoprobe and hotplugging.

View File

@ -9387,9 +9387,8 @@ S: Maintained
F: drivers/edac/octeon_edac*
EDAC-CAVIUM THUNDERX
M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org
S: Odd Fixes
S: Orphan
F: drivers/edac/thunderx_edac*
EDAC-CORE
@ -9416,9 +9415,8 @@ S: Supported
F: drivers/edac/dmc520_edac.c
EDAC-E752X
M: Mark Gross <markgross@kernel.org>
L: linux-edac@vger.kernel.org
S: Maintained
S: Orphan
F: drivers/edac/e752x_edac.c
EDAC-E7XXX
@ -9493,9 +9491,8 @@ S: Maintained
F: drivers/edac/igen6_edac.c
EDAC-MPC85XX
M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org
S: Maintained
S: Orphan
F: drivers/edac/mpc85xx_edac.[ch]
EDAC-NPCM
@ -13283,7 +13280,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h
INTEL KEEM BAY OCS ECC CRYPTO DRIVER
M: Prabhjot Khurana <prabhjot.khurana@intel.com>
M: Mark Gross <mgross@linux.intel.com>
S: Maintained
F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml
F: drivers/crypto/intel/keembay/Kconfig
@ -14240,8 +14236,6 @@ F: tools/testing/selftests/
KERNEL SMB3 SERVER (KSMBD)
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
M: Steve French <smfrench@gmail.com>
M: Steve French <sfrench@samba.org>
R: Sergey Senozhatsky <senozhatsky@chromium.org>
R: Tom Talpey <tom@talpey.com>
R: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
@ -18776,8 +18770,6 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: Documentation/scsi/g_NCR5380.rst
F: drivers/scsi/NCR5380.*
F: drivers/scsi/arm/cumana_1.c
F: drivers/scsi/arm/oak.c
F: drivers/scsi/atari_scsi.*
F: drivers/scsi/dmx3191d.c
F: drivers/scsi/g_NCR5380.*
@ -22949,7 +22941,7 @@ REAL TIME CLOCK (RTC) SUBSYSTEM
M: Alexandre Belloni <alexandre.belloni@bootlin.com>
L: linux-rtc@vger.kernel.org
S: Maintained
Q: http://patchwork.ozlabs.org/project/rtc-linux/list/
Q: https://patchwork.kernel.org/project/linux-rtc/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git
F: Documentation/admin-guide/rtc.rst
F: Documentation/devicetree/bindings/rtc/
@ -24571,7 +24563,7 @@ F: include/scsi/sg.h
SCSI SUBSYSTEM
M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
M: "Martin K. Petersen" <martin.petersen@oracle.com>
M: "Martin K. Petersen" <mkp@kernel.org>
L: linux-scsi@vger.kernel.org
S: Maintained
Q: https://patchwork.kernel.org/project/linux-scsi/list/
@ -24601,7 +24593,7 @@ F: drivers/target/target_core_user.c
F: include/uapi/linux/target_core_user.h
SCSI TARGET SUBSYSTEM
M: "Martin K. Petersen" <martin.petersen@oracle.com>
M: "Martin K. Petersen" <mkp@kernel.org>
L: linux-scsi@vger.kernel.org
L: target-devel@vger.kernel.org
S: Supported
@ -25238,10 +25230,9 @@ F: Documentation/admin-guide/LSM/Smack.rst
F: security/smack/
SMBDIRECT (RDMA Stream Transport with Read/Write-Offload, MS-SMBD)
M: Steve French <smfrench@gmail.com>
M: Steve French <sfrench@samba.org>
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
M: Paulo Alcantara <pc@manguebit.org>
R: Stefan Metzmacher <metze@samba.org>
R: Tom Talpey <tom@talpey.com>
L: linux-cifs@vger.kernel.org
@ -26543,6 +26534,7 @@ R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
L: linux-i2c@vger.kernel.org
S: Supported
F: drivers/i2c/busses/i2c-designware-*
F: include/linux/designware_i2c.h
SYNOPSYS DESIGNWARE I2C DRIVER - AMDISP
M: Nirujogi Pratap <pratap.nirujogi@amd.com>
@ -26955,8 +26947,7 @@ S: Maintained
F: drivers/net/ethernet/tehuti/tn40*
TELECOM CLOCK DRIVER FOR MCPL0010
M: Mark Gross <markgross@kernel.org>
S: Supported
S: Orphan
F: drivers/char/tlclk.c
TEMPO SEMICONDUCTOR DRIVERS
@ -29938,12 +29929,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h
XILINX VERSAL EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
F: drivers/edac/versal_edac.c
XILINX VERSALNET EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml
F: drivers/edac/versalnet_edac.c
@ -29989,6 +29982,7 @@ F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
XILINX ZYNQMP OCM EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
F: drivers/edac/zynqmp_edac.c

View File

@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 2
PATCHLEVEL = 3
SUBLEVEL = 0
EXTRAVERSION =
EXTRAVERSION = -rc1
NAME = Baby Opossum Posse
# *DOCUMENTATION*
@ -1083,6 +1083,16 @@ endif
export CC_FLAGS_SCS
endif
ifdef CONFIG_RUST_INLINE_HELPERS
# `rustc` normally emits traps for unreachable paths during code generation.
# With inline helpers, Clang performs code generation from the linked bitcode
# instead, so request the same behavior explicitly. Otherwise `objtool` may
# follow an impossible Rust path into the next function.
CC_FLAGS_RUST_INLINE_HELPERS := -mllvm -trap-unreachable \
-mllvm -no-trap-after-noreturn
export CC_FLAGS_RUST_INLINE_HELPERS
endif
ifdef CONFIG_LTO_CLANG
ifdef CONFIG_LTO_CLANG_FULL
CC_FLAGS_LTO := -flto
@ -1118,7 +1128,8 @@ endif
ifdef CONFIG_RUST
# Always pass -Zsanitizer-cfi-normalize-integers as CONFIG_RUST selects
# CONFIG_CFI_ICALL_NORMALIZE_INTEGERS.
RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers
# Disable function merging as LLVM incorrectly merges functions with different KCFI types.
RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zmerge-functions=disabled
KBUILD_RUSTFLAGS += $(RUSTC_FLAGS_CFI)
export RUSTC_FLAGS_CFI
endif

View File

@ -99,7 +99,7 @@ config ARM
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE
select HAVE_ARM_SMCCC if CPU_V7
select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32
select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 && !CPU_32v3
select HAVE_CONTEXT_TRACKING_USER
select HAVE_C_RECORDMCOUNT
select HAVE_BUILDTIME_MCOUNT_SORT
@ -134,6 +134,7 @@ config ARM
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
select HAVE_POSIX_CPU_TIMERS_TASK_WORK
select MMU_GATHER_RCU_TABLE_FREE if SMP && ARM_LPAE
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RSEQ

View File

@ -557,9 +557,16 @@ ENTRY(__switch_to)
ldmia r4, {r4 - sl, fp, ip, lr} @ Load all regs saved previously
#ifdef CONFIG_VMAP_STACK
@
@ Do a dummy read from the new stack while running from the old one so
@ that we can rely on do_translation_fault() to fix up any stale PMD
@ entries covering the vmalloc region.
@ For a non-lazy mm switch, check_vmalloc_seq() has ensured that
@ that the active mm's page tables have mappings for the prev
@ task's stack and the next task's stack.
@
@ For a lazy mm switch the active mm's page tables have mappings
@ for the prev task's stack but might not have mappings for the
@ new task's stack. Do a dummy read from the new stack while
@ running from the old stack so that we can rely on
@ do_translation_fault() to populate missing PMD entries covering the
@ new task's stack in the old task's page tables.
@
ldr r2, [ip]
#ifdef CONFIG_KASAN_VMALLOC

View File

@ -929,10 +929,6 @@ static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
break;
}
}
#else
static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
{
}
#endif
/*
@ -964,9 +960,14 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
case ARM_ENTRY_SYNC_WATCHPOINT:
watchpoint_handler(addr, fsr, regs);
break;
#ifdef CONFIG_CFI
case ARM_ENTRY_CFI_BREAKPOINT:
hw_breakpoint_cfi_handler(regs);
if (user_mode(regs))
ret = 1; /* Don't handle userspace BKPT */
else
hw_breakpoint_cfi_handler(regs);
break;
#endif
default:
ret = 1; /* Unhandled fault. */
}

View File

@ -181,7 +181,11 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
pr_err("8<--- cut here ---\n");
pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
tsk->comm, sig, addr, fsr);
show_pte(KERN_ERR, tsk->mm, addr);
if (likely(addr < TASK_SIZE)) {
mmap_write_lock(tsk->mm);
show_pte(KERN_ERR, tsk->mm, addr);
mmap_write_unlock(tsk->mm);
}
show_regs(regs);
}
#endif
@ -633,10 +637,21 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
return;
if (likely(user_mode(regs)))
local_irq_enable();
pr_alert("8<--- cut here ---\n");
pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
show_pte(KERN_ALERT, current->mm, addr);
if (likely(user_mode(regs))) {
if (addr < TASK_SIZE) {
mmap_write_lock(current->mm);
show_pte(KERN_ALERT, current->mm, addr);
mmap_write_unlock(current->mm);
}
} else {
show_pte(KERN_ALERT, current->mm, addr);
}
arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
fsr, 0);
@ -663,6 +678,9 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
return;
if (likely(user_mode(regs)))
local_irq_enable();
pr_alert("8<--- cut here ---\n");
pr_alert("Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
inf->name, ifsr, addr);

View File

@ -8,8 +8,6 @@
*
* These are the low level assembler for performing cache and TLB
* functions on the arm920.
*
* CONFIG_CPU_ARM920_CPU_IDLE -> nohlt
*/
#include <linux/linkage.h>
#include <linux/init.h>

View File

@ -9,8 +9,6 @@
*
* These are the low level assembler for performing cache and TLB
* functions on the arm922.
*
* CONFIG_CPU_ARM922_CPU_IDLE -> nohlt
*/
#include <linux/linkage.h>
#include <linux/init.h>

View File

@ -15,8 +15,6 @@
* These are the low level assembler for performing cache and TLB
* functions on the arm925.
*
* CONFIG_CPU_ARM925_CPU_IDLE -> nohlt
*
* Some additional notes based on deciphering the TI TRM on OMAP-5910:
*
* NOTE1: The TI925T Configuration Register bit "D-cache clean and flush

View File

@ -8,8 +8,6 @@
*
* These are the low level assembler for performing cache and TLB
* functions on the arm926.
*
* CONFIG_CPU_ARM926_CPU_IDLE -> nohlt
*/
#include <linux/linkage.h>
#include <linux/init.h>

View File

@ -1649,24 +1649,16 @@ static int __init run_all_tests(void)
return ret;
}
late_initcall(run_all_tests);
/*
* Module setup
*/
#ifdef MODULE
static void __exit kprobe_test_exit(void)
{
}
module_init(run_all_tests)
module_exit(kprobe_test_exit)
MODULE_DESCRIPTION("Test code for ARM kprobes");
MODULE_LICENSE("GPL");
#else /* !MODULE */
late_initcall(run_all_tests);
#endif

View File

@ -264,7 +264,7 @@ static inline void task_set_sve_vl_onexec(struct task_struct *task,
#define SCTLR_USER_MASK \
(SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | SCTLR_ELx_ENDA | SCTLR_ELx_ENDB | \
SCTLR_EL1_TCF0_MASK)
SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK)
static inline void arch_thread_struct_whitelist(unsigned long *offset,
unsigned long *size)

View File

@ -50,6 +50,8 @@ struct ptdump_pg_state {
const struct addr_marker *marker;
const struct mm_struct *mm;
unsigned long start_address;
/* exclusive end, ULONG_MAX represents an end at 1 << 64 */
unsigned long end_address;
int level;
ptval_t current_prot;
bool check_wx;

View File

@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
static int
do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd, rn, nr_regs, regbits;
unsigned long eaddr, newaddr;
unsigned int rd, rn, regbits;
unsigned long eaddr, newaddr, nr_regs;
unsigned int val;
/* count the number of registers in the mask to be transferred */

View File

@ -130,7 +130,7 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu
}
st->ipa_marker[0].name = "Guest IPA";
st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);
st->ipa_marker[1].start_address = ULONG_MAX;
st->mmu = mmu;
return st;
@ -148,18 +148,21 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
.flags = KVM_PGTABLE_WALK_LEAF,
};
guard(write_lock)(&kvm->mmu_lock);
st->parser_state = (struct ptdump_pg_state) {
.marker = &st->ipa_marker[0],
.end_address = BIT(mmu->pgt->ia_bits),
.level = -1,
.pg_level = &st->level[0],
.seq = m,
};
write_lock(&kvm->mmu_lock);
ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
write_unlock(&kvm->mmu_lock);
if (ret)
return ret;
note_page_flush(&st->parser_state.ptdump);
return ret;
return 0;
}
static int kvm_ptdump_guest_open(struct inode *m, struct file *file)

View File

@ -278,9 +278,19 @@ void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
void note_page_flush(struct ptdump_state *pt_st)
{
struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
unsigned long end = st->end_address;
pte_t pte_zero = {0};
note_page(pt_st, 0, -1, pte_val(pte_zero));
/*
* Address spaces that end at 1 << 64 have end_address == ULONG_MAX,
* but note_page() expects the exclusive end. In this case adjust end
* to the wraparound value 0.
*/
if (end == ULONG_MAX)
end = 0;
note_page(pt_st, end, -1, pte_val(pte_zero));
}
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
@ -296,6 +306,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
.marker = info->markers,
.mm = info->mm,
.pg_level = &kernel_pg_levels[0],
.end_address = end,
.level = -1,
.ptdump = {
.note_page_pte = note_page_pte,
@ -337,6 +348,7 @@ bool ptdump_check_wx(void)
{ -1, NULL},
},
.pg_level = &kernel_pg_levels[0],
.end_address = ~0UL,
.level = -1,
.check_wx = true,
.ptdump = {

View File

@ -12,7 +12,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/fec.h>
#include <linux/dmaengine.h>
#include <asm/traps.h>

View File

@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <asm/machdep.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
@ -242,6 +243,30 @@ static void __init m5441x_fec_init(void)
mcf_write8(0x03, MCFGPIO_PAR_FEC);
}
/*
* Reset Controller Module status register. Exposed to userspace as
* /sys/devices/platform/mcf-rcm-reset/power_on_reason by the mcf-rcm-reset
* driver (drivers/power/reset/mcf-rcm-reset.c).
*/
static struct resource m5441x_rcm_resource[] = {
{
.start = MCF_RSR,
.end = MCF_RSR,
.flags = IORESOURCE_MEM,
},
};
static int __init m5441x_rcm_init(void)
{
struct platform_device *pdev;
pdev = platform_device_register_simple("mcf-rcm-reset", -1,
m5441x_rcm_resource,
ARRAY_SIZE(m5441x_rcm_resource));
return PTR_ERR_OR_ZERO(pdev);
}
arch_initcall(m5441x_rcm_init);
void __init config_BSP(char *commandp, int size)
{
m5441x_clk_init();

View File

@ -0,0 +1,52 @@
CONFIG_LOG_BUF_SHIFT=14
CONFIG_EXPERT=y
# CONFIG_FUTEX is not set
# CONFIG_EPOLL is not set
# CONFIG_SIGNALFD is not set
# CONFIG_TIMERFD is not set
# CONFIG_EVENTFD is not set
# CONFIG_AIO is not set
# CONFIG_KALLSYMS is not set
# CONFIG_MMU is not set
CONFIG_M523x=y
CONFIG_M5235EVB=y
CONFIG_RAMBASE=0x00000000
CONFIG_RAMSIZE=0x01000000
CONFIG_VECTORBASE=0x00000000
CONFIG_IPSBAR=0x40000000
CONFIG_KERNELBASE=0x00020000
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_BINFMT_FLAT=y
# CONFIG_VM_EVENT_COUNTERS is not set
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_INET_DIAG is not set
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
CONFIG_MTD_UCLINUX=y
CONFIG_BLK_DEV_RAM=y
CONFIG_NETDEVICES=y
CONFIG_FEC=y
# CONFIG_INPUT is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
# CONFIG_UNIX98_PTYS is not set
CONFIG_SERIAL_MCF=y
CONFIG_SERIAL_MCF_CONSOLE=y
# CONFIG_HWMON is not set
# CONFIG_USB_SUPPORT is not set
CONFIG_EXT2_FS=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
CONFIG_ROMFS_FS=y
CONFIG_ROMFS_BACKED_BY_MTD=y
# CONFIG_NETWORK_FILESYSTEMS is not set
# CONFIG_SLUB_DEBUG is not set
CONFIG_BOOTPARAM=y
CONFIG_BOOTPARAM_STRING="root=/dev/mtdblock0"

View File

@ -0,0 +1,51 @@
CONFIG_LOG_BUF_SHIFT=14
CONFIG_EXPERT=y
# CONFIG_FUTEX is not set
# CONFIG_EPOLL is not set
# CONFIG_SIGNALFD is not set
# CONFIG_TIMERFD is not set
# CONFIG_EVENTFD is not set
# CONFIG_AIO is not set
# CONFIG_KALLSYMS is not set
# CONFIG_MMU is not set
CONFIG_M528x=y
CONFIG_RAMBASE=0x00000000
CONFIG_RAMSIZE=0x00800000
CONFIG_VECTORBASE=0x00000000
CONFIG_IPSBAR=0x40000000
CONFIG_KERNELBASE=0x00020000
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_BINFMT_FLAT=y
# CONFIG_VM_EVENT_COUNTERS is not set
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_INET_DIAG is not set
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
CONFIG_MTD_UCLINUX=y
CONFIG_BLK_DEV_RAM=y
CONFIG_NETDEVICES=y
CONFIG_FEC=y
# CONFIG_INPUT is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
# CONFIG_UNIX98_PTYS is not set
CONFIG_SERIAL_MCF=y
CONFIG_SERIAL_MCF_CONSOLE=y
# CONFIG_HWMON is not set
# CONFIG_USB_SUPPORT is not set
CONFIG_EXT2_FS=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
CONFIG_ROMFS_FS=y
CONFIG_ROMFS_BACKED_BY_MTD=y
# CONFIG_NETWORK_FILESYSTEMS is not set
# CONFIG_SLUB_DEBUG is not set
CONFIG_BOOTPARAM=y
CONFIG_BOOTPARAM_STRING="root=/dev/mtdblock0"

View File

@ -16,7 +16,7 @@ int __mcfgpio_request(unsigned gpio);
void __mcfgpio_free(unsigned gpio);
#ifdef CONFIG_GPIOLIB
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#else
/* our alternate 'gpiolib' functions */

View File

@ -1223,15 +1223,50 @@ _no_syscall_trace:
*
*/
/* Keep this literal; hi()/lo() can't use the UL-suffixed TASK_SIZE. */
#define OR1K_ATOMIC_ADDR_LIMIT 0x7ffffffc
ENTRY(sys_or1k_atomic)
/* FIXME: This ignores r3 and always does an XCHG */
/* Check both user pointers before accessing them. */
l.movhi r13,hi(OR1K_ATOMIC_ADDR_LIMIT)
l.ori r13,r13,lo(OR1K_ATOMIC_ADDR_LIMIT)
l.sfgtu r4,r13
l.bf 9f
l.nop
l.sfgtu r5,r13
l.bf 9f
l.nop
DISABLE_INTERRUPTS(r17,r19)
l.lwz r29,0(r4)
l.lwz r27,0(r5)
l.sw 0(r4),r27
l.sw 0(r5),r29
10: l.lwz r29,0(r4)
11: l.lwz r27,0(r5)
12: l.sw 0(r4),r27
13: l.sw 0(r5),r29
ENABLE_INTERRUPTS(r17)
l.jr r9
l.or r11,r0,r0
/*
* Either pointer was outside user space, or turned out to be
* unmapped/inaccessible when we actually touched it.
*/
9: l.jr r9
l.addi r11,r0,-EFAULT
.section .fixup, "ax"
14:
ENABLE_INTERRUPTS(r17)
l.j 9b
l.nop
.previous
.section __ex_table, "a"
.long 10b, 14b
.long 11b, 14b
.long 12b, 14b
.long 13b, 14b
.previous
/* ============================================================[ EOF ]=== */

View File

@ -74,11 +74,11 @@ void local_flush_tlb_all(void)
#define flush_dtlb_page_eir(addr) mtspr(SPR_DTLBEIR, addr)
#define flush_dtlb_page_no_eir(addr) \
mtspr_off(SPR_DTLBMR_BASE(0), DTLB_OFFSET(addr), 0);
mtspr_off(SPR_DTLBMR_BASE(0), DTLB_OFFSET(addr), 0)
#define flush_itlb_page_eir(addr) mtspr(SPR_ITLBEIR, addr)
#define flush_itlb_page_no_eir(addr) \
mtspr_off(SPR_ITLBMR_BASE(0), ITLB_OFFSET(addr), 0);
mtspr_off(SPR_ITLBMR_BASE(0), ITLB_OFFSET(addr), 0)
void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_TIMEX_H
#define __UM_TIMEX_H
/*
* Do not fall back to the host architecture header because latter likely
* includes facilities like cpu_feature_enabled() which are present only
* there. That would result in build breakages and/or efforts to "emulate"
* those facilities in UML.
*/
#include <asm-generic/timex.h>
#endif

View File

@ -33,7 +33,7 @@ extern void platform_setup (char **);
extern void platform_idle (void);
/*
* platform_calibrate_ccount calibrates cpu clock freq (CONFIG_XTENSA_CALIBRATE)
* platform_calibrate_ccount calibrates cpu clock freq (CONFIG_XTENSA_CALIBRATE_CCOUNT)
*/
extern void platform_calibrate_ccount (void);

View File

@ -21,7 +21,6 @@
#include <linux/irq.h>
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/profile.h>
#include <linux/sched/mm.h>
#include <linux/sched/hotplug.h>
#include <linux/sched/task_stack.h>
@ -459,12 +458,6 @@ void show_ipi_list(struct seq_file *p, int prec)
}
}
int setup_profiling_timer(unsigned int multiplier)
{
pr_debug("setup_profiling_timer %d\n", multiplier);
return 0;
}
/* TLB flush functions */
struct flush_data {

View File

@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/profile.h>
#include <linux/delay.h>
#include <linux/irqdomain.h>
#include <linux/sched_clock.h>

View File

@ -166,8 +166,9 @@ late_initcall(rs_init);
static void iss_console_write(struct console *co, const char *s, unsigned count)
{
if (s && *s != 0)
simc_write(1, s, min(count, strlen(s)));
count = s ? strnlen(s, count) : 0;
if (count)
simc_write(1, s, count);
}
static struct tty_driver* iss_console_device(struct console *c, int *index)

View File

@ -559,12 +559,22 @@ EXPORT_SYMBOL_GPL(acomp_walk_virt);
struct acomp_req *acomp_request_clone(struct acomp_req *req,
size_t total, gfp_t gfp)
{
struct crypto_tfm *tfm = req->base.tfm;
struct acomp_req *nreq;
size_t len;
nreq = container_of(crypto_request_clone(&req->base, total, gfp),
struct acomp_req, base);
if (nreq == req)
len = sizeof(*req) +
crypto_acomp_reqsize(crypto_acomp_reqtfm(req));
len = ALIGN(len, CRYPTO_MINALIGN);
nreq = kzalloc(len, gfp);
if (!nreq) {
req->base.tfm = tfm->fb;
return req;
}
memcpy(nreq, req, sizeof(*req));
nreq->base.flags &= ~CRYPTO_TFM_REQ_ON_STACK;
if (req->src == &req->chain.ssg)
nreq->src = &nreq->chain.ssg;

View File

@ -954,7 +954,6 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
INIT_LIST_HEAD(&resource->list_node);
INIT_LIST_HEAD(&resource->dependents);
device->power.state = ACPI_STATE_UNKNOWN;
device->flags.initialized = true;
/* Evaluate the object to get the system level and resource order. */
status = acpi_evaluate_object(handle, NULL, NULL, &buffer);

View File

@ -1144,6 +1144,9 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources))
device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
}
if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;
}
static void acpi_bus_get_flags(struct acpi_device *device)
@ -1825,6 +1828,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
acpi_set_pnp_ids(handle, &device->pnp, type);
acpi_init_properties(device);
acpi_bus_get_flags(device);
device->flags.initialized = true;
device->flags.enumeration_by_parent =
acpi_device_enumeration_by_parent(device);
acpi_device_clear_enumerated(device);

View File

@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
}
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
static u8 btintel_version_tlv_min_len(u8 type)
{
switch (type) {
case INTEL_TLV_CNVI_TOP:
case INTEL_TLV_CNVR_TOP:
case INTEL_TLV_CNVI_BT:
case INTEL_TLV_CNVR_BT:
case INTEL_TLV_BUILD_NUM:
case INTEL_TLV_GIT_SHA1:
return sizeof(u32);
case INTEL_TLV_DEV_REV_ID:
case INTEL_TLV_TIME_STAMP:
return sizeof(u16);
case INTEL_TLV_IMAGE_TYPE:
case INTEL_TLV_BUILD_TYPE:
case INTEL_TLV_SECURE_BOOT:
case INTEL_TLV_OTP_LOCK:
case INTEL_TLV_API_LOCK:
case INTEL_TLV_DEBUG_LOCK:
case INTEL_TLV_LIMITED_CCE:
case INTEL_TLV_SBE_TYPE:
return sizeof(u8);
case INTEL_TLV_MIN_FW:
return 3;
case INTEL_TLV_OTP_BDADDR:
return sizeof(bdaddr_t);
default:
return 0;
}
}
int btintel_parse_version_tlv(struct hci_dev *hdev,
struct intel_version_tlv *version,
struct sk_buff *skb)
{
/* Consume Command Complete Status field */
skb_pull(skb, 1);
if (!skb_pull(skb, 1))
return -EINVAL;
/* Event parameters contain multiple TLVs. Read each of them
* and only keep the required data. Also, it use existing legacy
@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
if (skb->len < tlv->len + sizeof(*tlv))
return -EINVAL;
if (tlv->len < btintel_version_tlv_min_len(tlv->type))
return -EINVAL;
switch (tlv->type) {
case INTEL_TLV_CNVI_TOP:
version->cnvi_top = get_unaligned_le32(tlv->val);
@ -667,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
break;
case INTEL_TLV_FW_ID:
snprintf(version->fw_id, sizeof(version->fw_id),
"%s", tlv->val);
"%.*s", tlv->len, tlv->val);
break;
default:
/* Ignore rest of information */
@ -686,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
{
struct sk_buff *skb;
const u8 param[1] = { 0xFF };
int err;
if (!version)
return -EINVAL;
@ -704,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
return -EIO;
}
btintel_parse_version_tlv(hdev, version, skb);
err = btintel_parse_version_tlv(hdev, version, skb);
kfree_skb(skb);
return 0;
return err;
}
/* ------- REGMAP IBT SUPPORT ------- */

View File

@ -1696,6 +1696,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (unlikely(!(intr_fh | intr_hw))) {
/* Ignore interrupt, inta == 0 */
bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n");
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
BIT(entry->entry));
return IRQ_NONE;
}

View File

@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(2000));
if (err == 1) {
if (err == -EINTR) {
bt_dev_err(hdev, "Firmware load interrupted");
err = -EINTR;
break;
} else if (err) {
bt_dev_err(hdev, "Firmware request timeout");

View File

@ -67,6 +67,15 @@ config IPMI_SI
Currently, only KCS and SMIC are supported. If
you are using IPMI, you should probably say "y" here.
config IPMI_SI_ASYNC_INIT
bool 'Asynchronous initialization of IPMI System Interface'
depends on IPMI_SI
default n
help
Offloads individual SMI inits. It speeds up the boot time.
It also introduces a very small risk that something else might fail
if it depends on synchronous IPMI init.
config IPMI_SSIF
tristate 'IPMI SMBus handler (SSIF)'
depends on I2C

View File

@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf,
u8 msg[MAX_MSG_LEN];
ssize_t ret;
if (count > sizeof(msg))
if (!count || count > sizeof(msg))
return -EINVAL;
if (copy_from_user(&msg, buf, count))
return -EFAULT;
if (count < msg[0])
if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN ||
count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1)
return -EINVAL;
rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]);
@ -360,8 +361,8 @@ MODULE_DEVICE_TABLE(i2c, ipmb_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id acpi_ipmb_id[] = {
{ "IPMB0001", 0 },
{},
{ .id = "IPMB0001" },
{ }
};
MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id);
#endif

View File

@ -1391,6 +1391,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
}
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
while (rcvrs) {
rcvr = rcvrs;
rcvrs = rcvr->next;
@ -3301,6 +3302,7 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf,
list_del(&intf->bmc_link);
mutex_unlock(&bmc->dyn_mutex);
intf->bmc = &intf->tmp_bmc;
ida_free(&ipmi_bmc_ida, bmc->pdev.id);
put_device(&bmc->pdev.dev);
goto out;
}
@ -3740,6 +3742,7 @@ int ipmi_add_smi(struct module *owner,
sysfs_attr_init(&intf->maintenance_mode_devattr.attr);
rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr);
if (rv) {
device_remove_file(intf->si_dev, &intf->nr_msgs_devattr);
device_remove_file(intf->si_dev, &intf->nr_users_devattr);
goto out_err_bmc_reg;
}
@ -3757,12 +3760,14 @@ int ipmi_add_smi(struct module *owner,
out_err_bmc_reg:
ipmi_bmc_unregister(intf);
out_err_started:
intf->in_shutdown = true;
if (intf->handlers->shutdown)
intf->handlers->shutdown(intf->send_info);
out_err:
list_del(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex);
mutex_unlock(&smi_watchers_mutex);
cancel_work_sync(&intf->smi_work);
kref_put(&intf->refcount, intf_free);
return rv;

View File

@ -39,6 +39,7 @@
#include <linux/rcupdate.h>
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
#include <linux/workqueue.h>
#include "ipmi_si.h"
#include "ipmi_si_sm.h"
#include <linux/string.h>
@ -252,6 +253,8 @@ struct smi_info {
struct task_struct *thread;
struct work_struct init_work;
struct list_head link;
};
@ -272,6 +275,7 @@ static bool unload_when_empty = true;
static int try_smi_init(struct smi_info *smi);
static void cleanup_one_si(struct smi_info *smi_info);
static void cleanup_ipmi_si(void);
static void smi_init_work_fn(struct work_struct *work);
#ifdef DEBUG_TIMING
void debug_timestamp(struct smi_info *smi_info, char *msg)
@ -1970,6 +1974,7 @@ int ipmi_si_add_smi(struct si_sm_io *io)
if (!new_smi)
return -ENOMEM;
spin_lock_init(&new_smi->si_lock);
INIT_WORK(&new_smi->init_work, smi_init_work_fn);
new_smi->io = *io;
@ -1982,7 +1987,12 @@ int ipmi_si_add_smi(struct si_sm_io *io)
dev_info(dup->io.dev,
"Removing SMBIOS-specified %s state machine in favor of ACPI\n",
si_to_str[new_smi->io.si_info->type]);
list_del(&dup->link);
mutex_unlock(&smi_infos_lock);
cleanup_one_si(dup);
mutex_lock(&smi_infos_lock);
} else {
dev_info(new_smi->io.dev,
"%s-specified %s state machine: duplicate\n",
@ -2000,8 +2010,12 @@ int ipmi_si_add_smi(struct si_sm_io *io)
list_add_tail(&new_smi->link, &smi_infos);
if (initialized)
rv = try_smi_init(new_smi);
if (initialized) {
if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT))
queue_work(system_dfl_wq, &new_smi->init_work);
else
rv = try_smi_init(new_smi);
}
out_err:
mutex_unlock(&smi_infos_lock);
return rv;
@ -2174,6 +2188,15 @@ static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2)
e1->io.addr_data == e2->io.addr_data);
}
static void smi_init_work_fn(struct work_struct *work)
{
struct smi_info *smi = container_of(work, struct smi_info, init_work);
mutex_lock(&smi_infos_lock);
try_smi_init(smi);
mutex_unlock(&smi_infos_lock);
}
static int __init init_ipmi_si(void)
{
struct smi_info *e, *e2;
@ -2219,8 +2242,12 @@ static int __init init_ipmi_si(void)
break;
}
}
if (!dup)
try_smi_init(e);
if (!dup) {
if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT))
queue_work(system_unbound_wq, &e->init_work);
else
try_smi_init(e);
}
}
/*
@ -2253,8 +2280,12 @@ static int __init init_ipmi_si(void)
break;
}
}
if (!dup)
try_smi_init(e);
if (!dup) {
if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT))
queue_work(system_unbound_wq, &e->init_work);
else
try_smi_init(e);
}
}
initialized = true;
@ -2344,31 +2375,36 @@ static void shutdown_smi(void *send_info)
}
/*
* Must be called with smi_infos_lock held, to serialize the
* smi_info->intf check.
* Must be called with smi_info unlinked from smi_infos and smi_infos_lock released.
*/
static void cleanup_one_si(struct smi_info *smi_info)
{
if (!smi_info)
return;
list_del(&smi_info->link);
if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT))
cancel_work_sync(&smi_info->init_work);
ipmi_unregister_smi(smi_info->intf);
kfree(smi_info);
}
void ipmi_si_remove_by_dev(struct device *dev)
{
struct smi_info *e;
struct smi_info *e = NULL, *tmp;
mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) {
if (e->io.dev == dev) {
cleanup_one_si(e);
list_for_each_entry(tmp, &smi_infos, link) {
if (tmp->io.dev == dev) {
e = tmp;
list_del(&e->link);
break;
}
}
mutex_unlock(&smi_infos_lock);
if (e)
cleanup_one_si(e);
}
struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
@ -2377,6 +2413,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
/* remove */
struct smi_info *e, *tmp_e;
struct device *dev = NULL;
LIST_HEAD(to_clean);
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
@ -2386,17 +2423,23 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
continue;
if (e->io.addr_data == addr) {
dev = get_device(e->io.dev);
cleanup_one_si(e);
list_move_tail(&e->link, &to_clean);
}
}
mutex_unlock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &to_clean, link) {
list_del(&e->link);
cleanup_one_si(e);
}
return dev;
}
static void cleanup_ipmi_si(void)
{
struct smi_info *e, *tmp_e;
LIST_HEAD(to_clean);
if (!initialized)
return;
@ -2410,10 +2453,14 @@ static void cleanup_ipmi_si(void)
ipmi_si_platform_shutdown();
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e);
list_splice_init(&smi_infos, &to_clean);
mutex_unlock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &to_clean, link) {
list_del(&e->link);
cleanup_one_si(e);
}
ipmi_si_hardcode_exit();
ipmi_si_hotmod_exit();
}

View File

@ -387,8 +387,8 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
}
static const struct acpi_device_id acpi_ipmi_match[] = {
{ "IPI0001", 0 },
{ },
{ .id = "IPI0001" },
{ }
};
MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match);
#else

View File

@ -2057,8 +2057,8 @@ static unsigned short *ssif_address_list(void)
#ifdef CONFIG_ACPI
static const struct acpi_device_id ssif_acpi_match[] = {
{ "IPI0001", 0 },
{ },
{ .id = "IPI0001" },
{ }
};
MODULE_DEVICE_TABLE(acpi, ssif_acpi_match);
#endif

View File

@ -163,8 +163,7 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
depends on HAS_IOPORT_MAP
depends on HAS_IOPORT
depends on X86
help
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver

View File

@ -93,7 +93,9 @@ static u8 st33zp24_status(struct tpm_chip *chip)
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
if (tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1) != 1)
return 0;
return data;
}
@ -104,10 +106,10 @@ static bool check_locality(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
u8 status;
int status;
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
if (status && (data &
if (status == 1 && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
return true;

View File

@ -3,94 +3,122 @@
* Handling of TPM command and other buffers.
*/
#include <linux/tpm_command.h>
#include <linux/module.h>
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include <linux/tpm_buf.h>
/**
* tpm_buf_init() - Allocate and initialize a TPM command
* @buf: A &tpm_buf
* @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
* @ordinal: A command ordinal
*
* Return: 0 or -ENOMEM
*/
int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
static void __tpm_buf_size_invariant(struct tpm_buf *buf, u16 buf_size)
{
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf->data)
return -ENOMEM;
u32 buf_size_2 = (u32)buf->capacity + (u32)sizeof(*buf);
tpm_buf_reset(buf, tag, ordinal);
return 0;
if (!buf->capacity) {
if (buf_size > TPM_BUFSIZE) {
WARN(1, "%s: size overflow: %u\n", __func__, buf_size);
buf->flags |= TPM_BUF_INVALID;
}
} else {
if (buf_size != buf_size_2) {
WARN(1, "%s: size mismatch: %u != %u\n", __func__,
buf_size, buf_size_2);
buf->flags |= TPM_BUF_INVALID;
}
}
}
EXPORT_SYMBOL_GPL(tpm_buf_init);
/**
* tpm_buf_reset() - Initialize a TPM command
* @buf: A &tpm_buf
* @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
* @ordinal: A command ordinal
*/
void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag,
u32 ordinal)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
__tpm_buf_size_invariant(buf, buf_size);
if (buf->flags & TPM_BUF_INVALID)
return;
WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
tag != TPM2_ST_SESSIONS && tag != 0);
buf->flags = 0;
buf->length = sizeof(*head);
buf->capacity = buf_size - sizeof(*buf);
buf->handles = 0;
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
buf->handles = 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_reset);
static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
{
__tpm_buf_size_invariant(buf, buf_size);
if (buf->flags & TPM_BUF_INVALID)
return;
buf->flags = TPM_BUF_TPM2B;
buf->length = 2;
buf->capacity = buf_size - sizeof(*buf);
buf->handles = 0;
buf->data[0] = 0;
buf->data[1] = 0;
}
/**
* tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
* @buf: A @tpm_buf
*
* Return: 0 or -ENOMEM
* tpm_buf_init() - Initialize a TPM command
* @buf: A &tpm_buf
* @buf_size: Size of the buffer.
*/
int tpm_buf_init_sized(struct tpm_buf *buf)
void tpm_buf_init(struct tpm_buf *buf, u16 buf_size)
{
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf->data)
return -ENOMEM;
memset(buf, 0, buf_size);
__tpm_buf_reset(buf, buf_size, TPM_TAG_RQU_COMMAND, 0);
}
EXPORT_SYMBOL_GPL(tpm_buf_init);
tpm_buf_reset_sized(buf);
return 0;
/**
* tpm_buf_init_sized() - Initialize a sized buffer
* @buf: A &tpm_buf
* @buf_size: Size of the buffer.
*/
void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size)
{
memset(buf, 0, buf_size);
__tpm_buf_reset_sized(buf, buf_size);
}
EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
/**
* tpm_buf_reset_sized() - Initialize a sized buffer
* tpm_buf_reset() - Re-initialize a TPM command
* @buf: A &tpm_buf
* @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
* @ordinal: A command ordinal
*/
void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
u16 buf_size = buf->capacity + sizeof(*buf);
__tpm_buf_reset(buf, buf_size, tag, ordinal);
}
EXPORT_SYMBOL_GPL(tpm_buf_reset);
/**
* tpm_buf_reset_sized() - Re-initialize a sized buffer
* @buf: A &tpm_buf
*/
void tpm_buf_reset_sized(struct tpm_buf *buf)
{
buf->flags = TPM_BUF_TPM2B;
buf->length = 2;
buf->data[0] = 0;
buf->data[1] = 0;
u16 buf_size = buf->capacity + sizeof(*buf);
__tpm_buf_reset_sized(buf, buf_size);
}
EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
void tpm_buf_destroy(struct tpm_buf *buf)
{
free_page((unsigned long)buf->data);
}
EXPORT_SYMBOL_GPL(tpm_buf_destroy);
/**
* tpm_buf_length() - Return the number of bytes consumed by the data
* @buf: A &tpm_buf
*
* Return: The number of bytes consumed by the buffer
*/
u32 tpm_buf_length(struct tpm_buf *buf)
u16 tpm_buf_length(struct tpm_buf *buf)
{
return buf->length;
}
@ -104,13 +132,14 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
*/
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
{
/* Return silently if overflow has already happened. */
if (buf->flags & TPM_BUF_OVERFLOW)
u32 total_length = (u32)buf->length + (u32)new_length;
if (buf->flags & TPM_BUF_INVALID)
return;
if ((buf->length + new_length) > PAGE_SIZE) {
if (total_length > (u32)buf->capacity) {
WARN(1, "tpm_buf: write overflow\n");
buf->flags |= TPM_BUF_OVERFLOW;
buf->flags |= TPM_BUF_INVALID;
return;
}
@ -148,17 +177,20 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
/**
* tpm_buf_append_handle() - Add a handle
* @chip: &tpm_chip instance
* @buf: &tpm_buf instance
* @handle: a TPM object handle
*
* Add a handle to the buffer, and increase the count tracking the number of
* handles in the command buffer. Works only for command buffers.
*/
void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle)
{
if (buf->flags & TPM_BUF_INVALID)
return;
if (buf->flags & TPM_BUF_TPM2B) {
dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
WARN(1, "tpm-buf: invalid type: TPM2B\n");
buf->flags |= TPM_BUF_INVALID;
return;
}
@ -177,14 +209,13 @@ static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void
{
off_t next_offset;
/* Return silently if overflow has already happened. */
if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
if (buf->flags & TPM_BUF_INVALID)
return;
next_offset = *offset + count;
if (next_offset > buf->length) {
WARN(1, "tpm_buf: read out of boundary\n");
buf->flags |= TPM_BUF_BOUNDARY_ERROR;
buf->flags |= TPM_BUF_INVALID;
return;
}

View File

@ -32,28 +32,35 @@ struct tpm_readpubek_out {
static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tpm_buf tpm_buf;
struct tpm_readpubek_out *out;
int i;
char *str = buf;
struct tpm_chip *chip = to_tpm_chip(dev);
char anti_replay[20];
struct tpm_buf *tpm_buf __free(kfree) = NULL;
memset(&anti_replay, 0, sizeof(anti_replay));
if (tpm_try_get_ops(chip))
return 0;
if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
goto out_ops;
tpm_buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!tpm_buf) {
tpm_put_ops(chip);
return 0;
}
tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
tpm_buf_init(tpm_buf, TPM_BUFSIZE);
tpm_buf_reset(tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
tpm_buf_append(tpm_buf, anti_replay, sizeof(anti_replay));
if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
"attempting to read the PUBEK"))
goto out_buf;
if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
"attempting to read the PUBEK")) {
tpm_put_ops(chip);
return 0;
}
out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
out = (struct tpm_readpubek_out *)&tpm_buf->data[10];
str +=
sprintf(str,
"Algorithm: %4ph\n"
@ -71,9 +78,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
for (i = 0; i < 256; i += 16)
str += sprintf(str, "%16ph\n", &out->modulus[i]);
out_buf:
tpm_buf_destroy(&tpm_buf);
out_ops:
tpm_put_ops(chip);
return str - buf;
}

View File

@ -33,7 +33,6 @@
#endif
#define TPM_MINOR 224 /* officially assigned */
#define TPM_BUFSIZE 4096
#define TPM_NUM_DEVICES 65536
#define TPM_RETRY 50
@ -52,185 +51,6 @@ enum tpm_addr {
TPM_ADDR = 0x4E,
};
#define TPM_WARN_RETRY 0x800
#define TPM_WARN_DOING_SELFTEST 0x802
#define TPM_ERR_DEACTIVATED 0x6
#define TPM_ERR_DISABLED 0x7
#define TPM_ERR_FAILEDSELFTEST 0x1C
#define TPM_ERR_INVALID_POSTINIT 38
#define TPM_TAG_RQU_COMMAND 193
/* TPM2 specific constants. */
#define TPM2_SPACE_BUFFER_SIZE 16384 /* 16 kB */
struct stclear_flags_t {
__be16 tag;
u8 deactivated;
u8 disableForceClear;
u8 physicalPresence;
u8 physicalPresenceLock;
u8 bGlobalLock;
} __packed;
struct tpm1_version {
u8 major;
u8 minor;
u8 rev_major;
u8 rev_minor;
} __packed;
struct tpm1_version2 {
__be16 tag;
struct tpm1_version version;
} __packed;
struct timeout_t {
__be32 a;
__be32 b;
__be32 c;
__be32 d;
} __packed;
struct duration_t {
__be32 tpm_short;
__be32 tpm_medium;
__be32 tpm_long;
} __packed;
struct permanent_flags_t {
__be16 tag;
u8 disable;
u8 ownership;
u8 deactivated;
u8 readPubek;
u8 disableOwnerClear;
u8 allowMaintenance;
u8 physicalPresenceLifetimeLock;
u8 physicalPresenceHWEnable;
u8 physicalPresenceCMDEnable;
u8 CEKPUsed;
u8 TPMpost;
u8 TPMpostLock;
u8 FIPS;
u8 operator;
u8 enableRevokeEK;
u8 nvLocked;
u8 readSRKPub;
u8 tpmEstablished;
u8 maintenanceDone;
u8 disableFullDALogicInfo;
} __packed;
typedef union {
struct permanent_flags_t perm_flags;
struct stclear_flags_t stclear_flags;
__u8 owned;
__be32 num_pcrs;
struct tpm1_version version1;
struct tpm1_version2 version2;
__be32 manufacturer_id;
struct timeout_t timeout;
struct duration_t duration;
} cap_t;
enum tpm_capabilities {
TPM_CAP_FLAG = 4,
TPM_CAP_PROP = 5,
TPM_CAP_VERSION_1_1 = 0x06,
TPM_CAP_VERSION_1_2 = 0x1A,
};
enum tpm_sub_capabilities {
TPM_CAP_PROP_PCR = 0x101,
TPM_CAP_PROP_MANUFACTURER = 0x103,
TPM_CAP_FLAG_PERM = 0x108,
TPM_CAP_FLAG_VOL = 0x109,
TPM_CAP_PROP_OWNER = 0x111,
TPM_CAP_PROP_TIS_TIMEOUT = 0x115,
TPM_CAP_PROP_TIS_DURATION = 0x120,
};
enum tpm2_pt_props {
TPM2_PT_NONE = 0x00000000,
TPM2_PT_GROUP = 0x00000100,
TPM2_PT_FIXED = TPM2_PT_GROUP * 1,
TPM2_PT_FAMILY_INDICATOR = TPM2_PT_FIXED + 0,
TPM2_PT_LEVEL = TPM2_PT_FIXED + 1,
TPM2_PT_REVISION = TPM2_PT_FIXED + 2,
TPM2_PT_DAY_OF_YEAR = TPM2_PT_FIXED + 3,
TPM2_PT_YEAR = TPM2_PT_FIXED + 4,
TPM2_PT_MANUFACTURER = TPM2_PT_FIXED + 5,
TPM2_PT_VENDOR_STRING_1 = TPM2_PT_FIXED + 6,
TPM2_PT_VENDOR_STRING_2 = TPM2_PT_FIXED + 7,
TPM2_PT_VENDOR_STRING_3 = TPM2_PT_FIXED + 8,
TPM2_PT_VENDOR_STRING_4 = TPM2_PT_FIXED + 9,
TPM2_PT_VENDOR_TPM_TYPE = TPM2_PT_FIXED + 10,
TPM2_PT_FIRMWARE_VERSION_1 = TPM2_PT_FIXED + 11,
TPM2_PT_FIRMWARE_VERSION_2 = TPM2_PT_FIXED + 12,
TPM2_PT_INPUT_BUFFER = TPM2_PT_FIXED + 13,
TPM2_PT_HR_TRANSIENT_MIN = TPM2_PT_FIXED + 14,
TPM2_PT_HR_PERSISTENT_MIN = TPM2_PT_FIXED + 15,
TPM2_PT_HR_LOADED_MIN = TPM2_PT_FIXED + 16,
TPM2_PT_ACTIVE_SESSIONS_MAX = TPM2_PT_FIXED + 17,
TPM2_PT_PCR_COUNT = TPM2_PT_FIXED + 18,
TPM2_PT_PCR_SELECT_MIN = TPM2_PT_FIXED + 19,
TPM2_PT_CONTEXT_GAP_MAX = TPM2_PT_FIXED + 20,
TPM2_PT_NV_COUNTERS_MAX = TPM2_PT_FIXED + 22,
TPM2_PT_NV_INDEX_MAX = TPM2_PT_FIXED + 23,
TPM2_PT_MEMORY = TPM2_PT_FIXED + 24,
TPM2_PT_CLOCK_UPDATE = TPM2_PT_FIXED + 25,
TPM2_PT_CONTEXT_HASH = TPM2_PT_FIXED + 26,
TPM2_PT_CONTEXT_SYM = TPM2_PT_FIXED + 27,
TPM2_PT_CONTEXT_SYM_SIZE = TPM2_PT_FIXED + 28,
TPM2_PT_ORDERLY_COUNT = TPM2_PT_FIXED + 29,
TPM2_PT_MAX_COMMAND_SIZE = TPM2_PT_FIXED + 30,
TPM2_PT_MAX_RESPONSE_SIZE = TPM2_PT_FIXED + 31,
TPM2_PT_MAX_DIGEST = TPM2_PT_FIXED + 32,
TPM2_PT_MAX_OBJECT_CONTEXT = TPM2_PT_FIXED + 33,
TPM2_PT_MAX_SESSION_CONTEXT = TPM2_PT_FIXED + 34,
TPM2_PT_PS_FAMILY_INDICATOR = TPM2_PT_FIXED + 35,
TPM2_PT_PS_LEVEL = TPM2_PT_FIXED + 36,
TPM2_PT_PS_REVISION = TPM2_PT_FIXED + 37,
TPM2_PT_PS_DAY_OF_YEAR = TPM2_PT_FIXED + 38,
TPM2_PT_PS_YEAR = TPM2_PT_FIXED + 39,
TPM2_PT_SPLIT_MAX = TPM2_PT_FIXED + 40,
TPM2_PT_TOTAL_COMMANDS = TPM2_PT_FIXED + 41,
TPM2_PT_LIBRARY_COMMANDS = TPM2_PT_FIXED + 42,
TPM2_PT_VENDOR_COMMANDS = TPM2_PT_FIXED + 43,
TPM2_PT_NV_BUFFER_MAX = TPM2_PT_FIXED + 44,
TPM2_PT_MODES = TPM2_PT_FIXED + 45,
TPM2_PT_MAX_CAP_BUFFER = TPM2_PT_FIXED + 46,
TPM2_PT_VAR = TPM2_PT_GROUP * 2,
TPM2_PT_PERMANENT = TPM2_PT_VAR + 0,
TPM2_PT_STARTUP_CLEAR = TPM2_PT_VAR + 1,
TPM2_PT_HR_NV_INDEX = TPM2_PT_VAR + 2,
TPM2_PT_HR_LOADED = TPM2_PT_VAR + 3,
TPM2_PT_HR_LOADED_AVAIL = TPM2_PT_VAR + 4,
TPM2_PT_HR_ACTIVE = TPM2_PT_VAR + 5,
TPM2_PT_HR_ACTIVE_AVAIL = TPM2_PT_VAR + 6,
TPM2_PT_HR_TRANSIENT_AVAIL = TPM2_PT_VAR + 7,
TPM2_PT_HR_PERSISTENT = TPM2_PT_VAR + 8,
TPM2_PT_HR_PERSISTENT_AVAIL = TPM2_PT_VAR + 9,
TPM2_PT_NV_COUNTERS = TPM2_PT_VAR + 10,
TPM2_PT_NV_COUNTERS_AVAIL = TPM2_PT_VAR + 11,
TPM2_PT_ALGORITHM_SET = TPM2_PT_VAR + 12,
TPM2_PT_LOADED_CURVES = TPM2_PT_VAR + 13,
TPM2_PT_LOCKOUT_COUNTER = TPM2_PT_VAR + 14,
TPM2_PT_MAX_AUTH_FAIL = TPM2_PT_VAR + 15,
TPM2_PT_LOCKOUT_INTERVAL = TPM2_PT_VAR + 16,
TPM2_PT_LOCKOUT_RECOVERY = TPM2_PT_VAR + 17,
TPM2_PT_NV_WRITE_RECOVERY = TPM2_PT_VAR + 18,
TPM2_PT_AUDIT_COUNTER_0 = TPM2_PT_VAR + 19,
TPM2_PT_AUDIT_COUNTER_1 = TPM2_PT_VAR + 20,
};
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
* bytes, but 128 is still a relatively large number of random bytes and
* anything much bigger causes users of struct tpm_cmd_t to start getting
* compiler warnings about stack frame size. */
#define TPM_MAX_RNG_DATA 128
extern const struct class tpm_class;
extern const struct class tpmrm_class;
extern dev_t tpm_devt;

View File

@ -22,8 +22,6 @@
#include "tpm.h"
#define TPM_MAX_ORDINAL 243
/*
* Array with one entry per ordinal defining the maximum amount
* of time the chip could take to return the result. The ordinal
@ -308,9 +306,6 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
return duration;
}
#define TPM_ORD_STARTUP 153
#define TPM_ST_CLEAR 1
/**
* tpm1_startup() - turn on the TPM
* @chip: TPM chip to use
@ -323,20 +318,18 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
*/
static int tpm1_startup(struct tpm_chip *chip)
{
struct tpm_buf buf;
int rc;
struct tpm_buf *buf __free(kfree) = NULL;
dev_info(&chip->dev, "starting up the TPM manually\n");
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
if (rc < 0)
return rc;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
tpm_buf_destroy(&buf);
return rc;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
tpm_buf_append_u16(buf, TPM_ST_CLEAR);
return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
}
int tpm1_get_timeouts(struct tpm_chip *chip)
@ -459,64 +452,56 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
return 0;
}
#define TPM_ORD_PCR_EXTEND 20
int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
const char *log_msg)
{
struct tpm_buf buf;
int rc;
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
if (rc)
return rc;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
tpm_buf_append_u32(buf, pcr_idx);
tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
tpm_buf_append_u32(&buf, pcr_idx);
tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
tpm_buf_destroy(&buf);
return rc;
return tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, log_msg);
}
#define TPM_ORD_GET_CAP 101
ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc, size_t min_cap_length)
{
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
if (rc)
return rc;
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
if (subcap_id == TPM_CAP_VERSION_1_1 ||
subcap_id == TPM_CAP_VERSION_1_2) {
tpm_buf_append_u32(&buf, subcap_id);
tpm_buf_append_u32(&buf, 0);
tpm_buf_append_u32(buf, subcap_id);
tpm_buf_append_u32(buf, 0);
} else {
if (subcap_id == TPM_CAP_FLAG_PERM ||
subcap_id == TPM_CAP_FLAG_VOL)
tpm_buf_append_u32(&buf, TPM_CAP_FLAG);
tpm_buf_append_u32(buf, TPM_CAP_FLAG);
else
tpm_buf_append_u32(&buf, TPM_CAP_PROP);
tpm_buf_append_u32(buf, TPM_CAP_PROP);
tpm_buf_append_u32(&buf, 4);
tpm_buf_append_u32(&buf, subcap_id);
tpm_buf_append_u32(buf, 4);
tpm_buf_append_u32(buf, subcap_id);
}
rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
rc = tpm_transmit_cmd(chip, buf, min_cap_length, desc);
if (!rc)
*cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
tpm_buf_destroy(&buf);
*cap = *(cap_t *)&buf->data[TPM_HEADER_SIZE + 4];
return rc;
}
EXPORT_SYMBOL_GPL(tpm1_getcap);
#define TPM_ORD_GET_RANDOM 70
struct tpm1_get_random_out {
__be32 rng_data_len;
u8 rng_data[TPM_MAX_RNG_DATA];
} __packed;
/**
* tpm1_get_random() - get random bytes from the TPM's RNG
* @chip: a &struct tpm_chip instance
@ -530,86 +515,80 @@ struct tpm1_get_random_out {
int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm1_get_random_out *out;
struct tpm_buf *buf __free(kfree) = NULL;
u32 num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
struct tpm_buf buf;
u32 total = 0;
int retries = 5;
u32 recd;
int rc;
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
if (rc)
return rc;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
do {
tpm_buf_append_u32(&buf, num_bytes);
tpm_buf_append_u32(buf, num_bytes);
rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len),
"attempting get random");
if (rc) {
if (rc > 0)
rc = -EIO;
goto out;
return rc;
}
out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
out = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];
recd = be32_to_cpu(out->rng_data_len);
if (recd > num_bytes) {
rc = -EFAULT;
goto out;
}
if (recd > num_bytes)
return -EFAULT;
if (tpm_buf_length(buf) < TPM_HEADER_SIZE +
sizeof(out->rng_data_len) + recd)
return -EFAULT;
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
sizeof(out->rng_data_len) + recd) {
rc = -EFAULT;
goto out;
}
memcpy(dest, out->rng_data, recd);
dest += recd;
total += recd;
num_bytes -= recd;
tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
} while (retries-- && total < max);
rc = total ? (int)total : -EIO;
out:
tpm_buf_destroy(&buf);
return rc;
}
#define TPM_ORD_PCRREAD 21
int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
{
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_READ);
tpm_buf_append_u32(buf, pcr_idx);
rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE,
"attempting to read a pcr value");
if (rc)
return rc;
tpm_buf_append_u32(&buf, pcr_idx);
if (tpm_buf_length(buf) < TPM_DIGEST_SIZE)
return -EFAULT;
rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
"attempting to read a pcr value");
if (rc)
goto out;
memcpy(res_buf, &buf->data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
if (tpm_buf_length(&buf) < TPM_DIGEST_SIZE) {
rc = -EFAULT;
goto out;
}
memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
out:
tpm_buf_destroy(&buf);
return rc;
}
#define TPM_ORD_CONTINUE_SELFTEST 83
/**
* tpm1_continue_selftest() - run TPM's selftest
* @chip: TPM chip to use
@ -619,16 +598,13 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
*/
static int tpm1_continue_selftest(struct tpm_chip *chip)
{
struct tpm_buf buf;
int rc;
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
if (rc)
return rc;
rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
tpm_buf_destroy(&buf);
return rc;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
return tpm_transmit_cmd(chip, buf, 0, "continue selftest");
}
/**
@ -726,8 +702,6 @@ int tpm1_auto_startup(struct tpm_chip *chip)
return rc;
}
#define TPM_ORD_SAVESTATE 152
/**
* tpm1_pm_suspend() - pm suspend handler
* @chip: TPM chip to use.
@ -742,22 +716,24 @@ int tpm1_auto_startup(struct tpm_chip *chip)
int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
{
u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
struct tpm_buf buf;
unsigned int try;
struct tpm_buf *buf __free(kfree) = NULL;
int rc;
/* for buggy tpm, flush pcrs with extend to selected dummy */
if (tpm_suspend_pcr)
rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
"extending dummy pcr before suspend");
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
if (rc)
return rc;
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
rc = tpm_transmit_cmd(chip, buf, 0, NULL);
/*
* If the TPM indicates that it is too busy to respond to
* this command then retry before giving up. It can take
@ -772,7 +748,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
break;
tpm_msleep(TPM_TIMEOUT_RETRY);
tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
}
if (rc)
@ -782,8 +758,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
dev_warn(&chip->dev, "TPM savestate took %dms\n",
try * TPM_TIMEOUT_RETRY);
tpm_buf_destroy(&buf);
return rc;
}

View File

@ -94,17 +94,6 @@ unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
}
struct tpm2_pcr_read_out {
__be32 update_cnt;
__be32 pcr_selects_cnt;
__be16 hash_alg;
u8 pcr_select_size;
u8 pcr_select[TPM2_PCR_SELECT_MIN];
__be32 digests_cnt;
__be16 digest_size;
u8 digest[];
} __packed;
/**
* tpm2_pcr_read() - read a PCR value
* @chip: TPM chip to use.
@ -119,12 +108,13 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
{
int i;
int rc;
struct tpm_buf buf;
struct tpm2_pcr_read_out *out;
u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
u16 digest_size;
u16 expected_digest_size = 0;
struct tpm_buf *buf __free(kfree) = NULL;
if (pcr_idx >= TPM2_PLATFORM_PCR)
return -EINVAL;
@ -139,36 +129,35 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
expected_digest_size = chip->allocated_banks[i].digest_size;
}
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
if (rc)
return rc;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
tpm_buf_append_u32(&buf, 1);
tpm_buf_append_u16(&buf, digest->alg_id);
tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
tpm_buf_append(&buf, (const unsigned char *)pcr_select,
tpm_buf_append_u32(buf, 1);
tpm_buf_append_u16(buf, digest->alg_id);
tpm_buf_append_u8(buf, TPM2_PCR_SELECT_MIN);
tpm_buf_append(buf, (const unsigned char *)pcr_select,
sizeof(pcr_select));
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
rc = tpm_transmit_cmd(chip, buf, 0, "attempting to read a pcr value");
if (rc)
goto out;
return rc;
out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
out = (struct tpm2_pcr_read_out *)&buf->data[TPM_HEADER_SIZE];
digest_size = be16_to_cpu(out->digest_size);
if (digest_size > sizeof(digest->digest) ||
(!digest_size_ptr && digest_size != expected_digest_size)) {
rc = -EINVAL;
goto out;
}
(!digest_size_ptr && digest_size != expected_digest_size))
return -EINVAL;
if (digest_size_ptr)
*digest_size_ptr = digest_size;
memcpy(digest->digest, out->digest, digest_size);
out:
tpm_buf_destroy(&buf);
return rc;
}
@ -184,7 +173,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests)
{
struct tpm_buf buf;
struct tpm_buf *buf __free(kfree) = NULL;
int rc;
int i;
@ -194,55 +183,49 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
return rc;
}
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc) {
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf) {
if (!disable_pcr_integrity)
tpm2_end_auth_session(chip);
return rc;
return -ENOMEM;
}
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (!disable_pcr_integrity) {
rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
if (rc) {
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return rc;
}
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
tpm_buf_append_auth(chip, &buf, NULL, 0);
tpm_buf_append_handle(buf, pcr_idx);
tpm_buf_append_auth(chip, buf, NULL, 0);
}
tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
tpm_buf_append_u32(buf, chip->nr_allocated_banks);
for (i = 0; i < chip->nr_allocated_banks; i++) {
tpm_buf_append_u16(&buf, digests[i].alg_id);
tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
tpm_buf_append_u16(buf, digests[i].alg_id);
tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
chip->allocated_banks[i].digest_size);
}
if (!disable_pcr_integrity) {
rc = tpm_buf_fill_hmac_session(chip, &buf);
if (rc) {
tpm_buf_destroy(&buf);
rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
return rc;
}
}
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
rc = tpm_transmit_cmd(chip, buf, 0, "attempting extend a PCR value");
if (!disable_pcr_integrity)
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
tpm_buf_destroy(&buf);
rc = tpm_buf_check_hmac_response(chip, buf, rc);
return rc;
}
struct tpm2_get_random_out {
__be16 size;
u8 buffer[TPM_MAX_RNG_DATA];
} __packed;
/**
* tpm2_get_random() - get random bytes from the TPM RNG
*
@ -258,7 +241,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm2_get_random_out *out;
struct tpm_header *head;
struct tpm_buf buf;
u32 recd;
u32 num_bytes = max;
int err;
@ -267,6 +249,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
u8 *dest_ptr = dest;
off_t offset;
struct tpm_buf *buf __free(kfree) = NULL;
if (!num_bytes || max > TPM_MAX_RNG_DATA)
return -EINVAL;
@ -274,55 +258,58 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
if (err)
return err;
err = tpm_buf_init(&buf, 0, 0);
if (err) {
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf) {
tpm2_end_auth_session(chip);
return err;
return -ENOMEM;
}
tpm_buf_init(buf, TPM_BUFSIZE);
do {
tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, &buf,
tpm_buf_append_hmac_session(chip, buf,
TPM2_SA_ENCRYPT |
TPM2_SA_CONTINUE_SESSION,
NULL, 0);
} else {
offset = buf.handles * 4 + TPM_HEADER_SIZE;
head = (struct tpm_header *)buf.data;
if (tpm_buf_length(&buf) == offset)
offset = buf->handles * 4 + TPM_HEADER_SIZE;
head = (struct tpm_header *)buf->data;
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
tpm_buf_append_u16(&buf, num_bytes);
err = tpm_buf_fill_hmac_session(chip, &buf);
tpm_buf_append_u16(buf, num_bytes);
err = tpm_buf_fill_hmac_session(chip, buf);
if (err)
goto out;
return err;
err = tpm_transmit_cmd(chip, &buf,
err = tpm_transmit_cmd(chip, buf,
offsetof(struct tpm2_get_random_out,
buffer),
"attempting get random");
err = tpm_buf_check_hmac_response(chip, &buf, err);
err = tpm_buf_check_hmac_response(chip, buf, err);
if (err) {
if (err > 0)
err = -EIO;
goto out;
tpm2_end_auth_session(chip);
return err;
}
head = (struct tpm_header *)buf.data;
head = (struct tpm_header *)buf->data;
offset = TPM_HEADER_SIZE;
/* Skip the parameter size field: */
if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
offset += 4;
out = (struct tpm2_get_random_out *)&buf.data[offset];
out = (struct tpm2_get_random_out *)&buf->data[offset];
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
if (tpm_buf_length(&buf) <
if (tpm_buf_length(buf) <
TPM_HEADER_SIZE +
offsetof(struct tpm2_get_random_out, buffer) +
recd) {
err = -EFAULT;
goto out;
tpm2_end_auth_session(chip);
return -EFAULT;
}
memcpy(dest_ptr, out->buffer, recd);
@ -331,13 +318,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
num_bytes -= recd;
} while (retries-- && total < max);
tpm_buf_destroy(&buf);
return total ? total : -EIO;
out:
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return err;
}
/**
@ -347,31 +328,21 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
*/
void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
{
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
if (rc) {
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf) {
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
handle);
return;
}
tpm_buf_append_u32(&buf, handle);
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
tpm_buf_append_u32(buf, handle);
tpm_transmit_cmd(chip, &buf, 0, "flushing context");
tpm_buf_destroy(&buf);
tpm_transmit_cmd(chip, buf, 0, "flushing context");
}
EXPORT_SYMBOL_GPL(tpm2_flush_context);
struct tpm2_get_cap_out {
u8 more_data;
__be32 subcap_id;
__be32 property_cnt;
__be32 property_id;
__be32 value;
} __packed;
/**
* tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property
* @chip: a &tpm_chip instance
@ -387,19 +358,21 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
const char *desc)
{
struct tpm2_get_cap_out *out;
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
if (rc)
return rc;
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(&buf, property_id);
tpm_buf_append_u32(&buf, 1);
rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(buf, property_id);
tpm_buf_append_u32(buf, 1);
rc = tpm_transmit_cmd(chip, buf, 0, NULL);
if (!rc) {
out = (struct tpm2_get_cap_out *)
&buf.data[TPM_HEADER_SIZE];
&buf->data[TPM_HEADER_SIZE];
/*
* To prevent failing boot up of some systems, Infineon TPM2.0
* returns SUCCESS on TPM2_Startup in field upgrade mode. Also
@ -411,7 +384,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
else
rc = -ENODATA;
}
tpm_buf_destroy(&buf);
return rc;
}
EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
@ -428,15 +400,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
*/
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
{
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
if (rc)
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return;
tpm_buf_append_u16(&buf, shutdown_type);
tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
tpm_buf_destroy(&buf);
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
tpm_buf_append_u16(buf, shutdown_type);
tpm_transmit_cmd(chip, buf, 0, "stopping the TPM");
}
/**
@ -454,20 +425,21 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
*/
static int tpm2_do_selftest(struct tpm_chip *chip)
{
struct tpm_buf buf;
int full;
int rc;
for (full = 0; full < 2; full++) {
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
if (rc)
return rc;
struct tpm_buf *buf __free(kfree) = NULL;
tpm_buf_append_u8(&buf, full);
rc = tpm_transmit_cmd(chip, &buf, 0,
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
tpm_buf_append_u8(buf, full);
rc = tpm_transmit_cmd(chip, buf, 0,
"attempting the self test");
tpm_buf_destroy(&buf);
if (rc == TPM2_RC_TESTING)
rc = TPM2_RC_SUCCESS;
if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
@ -492,23 +464,24 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
int tpm2_probe(struct tpm_chip *chip)
{
struct tpm_header *out;
struct tpm_buf buf;
int rc;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
if (rc)
return rc;
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
tpm_buf_append_u32(&buf, 1);
rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(buf, TPM_PT_TOTAL_COMMANDS);
tpm_buf_append_u32(buf, 1);
rc = tpm_transmit_cmd(chip, buf, 0, NULL);
/* We ignore TPM return codes on purpose. */
if (rc >= 0) {
out = (struct tpm_header *)buf.data;
out = (struct tpm_header *)buf->data;
if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
chip->flags |= TPM_CHIP_FLAG_TPM2;
}
tpm_buf_destroy(&buf);
return 0;
}
EXPORT_SYMBOL_GPL(tpm2_probe);
@ -539,16 +512,9 @@ static int tpm2_init_bank_info(struct tpm_chip *chip, u32 bank_index)
return tpm2_pcr_read(chip, 0, &digest, &bank->digest_size);
}
struct tpm2_pcr_selection {
__be16 hash_alg;
u8 size_of_select;
u8 pcr_select[3];
} __packed;
ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
{
struct tpm2_pcr_selection pcr_selection;
struct tpm_buf buf;
void *marker;
void *end;
void *pcr_select_offset;
@ -560,31 +526,32 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
int rc;
int i = 0;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
tpm_buf_append_u32(buf, TPM2_CAP_PCRS);
tpm_buf_append_u32(buf, 0);
tpm_buf_append_u32(buf, 1);
rc = tpm_transmit_cmd(chip, buf, 9, "get tpm pcr allocation");
if (rc)
return rc;
tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
tpm_buf_append_u32(&buf, 0);
tpm_buf_append_u32(&buf, 1);
rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
if (rc)
goto out;
nr_possible_banks = be32_to_cpup(
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
(__be32 *)&buf->data[TPM_HEADER_SIZE + 5]);
if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
pr_err("tpm: out of bank capacity: %u > %u\n",
nr_possible_banks, TPM2_MAX_PCR_BANKS);
rc = -ENOMEM;
goto out;
return -ENOMEM;
}
marker = &buf.data[TPM_HEADER_SIZE + 9];
marker = &buf->data[TPM_HEADER_SIZE + 9];
rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
end = &buf.data[rsp_len];
rsp_len = be32_to_cpup((__be32 *)&buf->data[2]);
end = &buf->data[rsp_len];
for (i = 0; i < nr_possible_banks; i++) {
pcr_select_offset = marker +
@ -616,21 +583,20 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
}
chip->nr_allocated_banks = nr_alloc_banks;
out:
tpm_buf_destroy(&buf);
return rc;
}
int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
{
struct tpm_buf buf;
u32 nr_commands;
__be32 *attrs;
u32 cc;
int i;
int rc;
struct tpm_buf *buf __free(kfree) = NULL;
rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
if (rc)
goto out;
@ -647,30 +613,31 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
goto out;
}
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
if (rc)
goto out;
tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
tpm_buf_append_u32(&buf, nr_commands);
rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
if (rc) {
tpm_buf_destroy(&buf);
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
tpm_buf_append_u32(buf, TPM2_CAP_COMMANDS);
tpm_buf_append_u32(buf, TPM2_CC_FIRST);
tpm_buf_append_u32(buf, nr_commands);
rc = tpm_transmit_cmd(chip, buf, 9 + 4 * nr_commands, NULL);
if (rc)
goto out;
if (nr_commands !=
be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5])) {
rc = -EFAULT;
tpm_buf_destroy(&buf);
goto out;
}
chip->nr_commands = nr_commands;
attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
attrs = (__be32 *)&buf->data[TPM_HEADER_SIZE + 9];
for (i = 0; i < nr_commands; i++, attrs++) {
chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
cc = chip->cc_attrs_tbl[i] & 0xFFFF;
@ -682,8 +649,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
}
}
tpm_buf_destroy(&buf);
out:
if (rc > 0)
rc = -ENODEV;
@ -704,20 +669,18 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
static int tpm2_startup(struct tpm_chip *chip)
{
struct tpm_buf buf;
int rc;
struct tpm_buf *buf __free(kfree) = NULL;
dev_info(&chip->dev, "starting up the TPM manually\n");
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
if (rc < 0)
return rc;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
tpm_buf_destroy(&buf);
return rc;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
}
/**

View File

@ -167,8 +167,8 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf *buf __free(kfree) = NULL;
int rc, name_size_alg;
struct tpm_buf buf;
if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
mso != TPM2_MSO_NVRAM) {
@ -176,50 +176,40 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
return sizeof(u32);
}
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
tpm_buf_append_u32(buf, handle);
rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
if (rc)
return rc;
tpm_buf_append_u32(&buf, handle);
rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
if (rc) {
tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
/* Skip TPMT_PUBLIC: */
offset += tpm_buf_read_u16(&buf, &offset);
offset += tpm_buf_read_u16(buf, &offset);
/*
* Ensure space for the length field of TPM2B_NAME and hashAlg field of
* TPMT_HA (the extra four bytes).
*/
if (offset + 4 > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
if (offset + 4 > tpm_buf_length(buf))
return -EIO;
}
rc = tpm_buf_read_u16(&buf, &offset);
name_size_alg = name_size(&buf.data[offset]);
if (name_size_alg < 0) {
tpm_buf_destroy(&buf);
rc = tpm_buf_read_u16(buf, &offset);
name_size_alg = name_size(&buf->data[offset]);
if (name_size_alg < 0)
return name_size_alg;
}
if (rc != name_size_alg) {
tpm_buf_destroy(&buf);
if (rc != name_size_alg)
return -EIO;
}
if (offset + rc > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
if (offset + rc > tpm_buf_length(buf))
return -EIO;
}
memcpy(name, &buf.data[offset], rc);
tpm_buf_destroy(&buf);
memcpy(name, &buf->data[offset], rc);
return name_size_alg;
}
#endif /* CONFIG_TCG_TPM2_HMAC */
@ -260,7 +250,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
tpm_buf_append_handle(buf, handle);
return 0;
}
@ -1005,8 +995,8 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
*/
int tpm2_start_auth_session(struct tpm_chip *chip)
{
struct tpm_buf *buf __free(kfree) = NULL;
struct tpm2_auth *auth;
struct tpm_buf buf;
u32 null_key;
int rc;
@ -1020,58 +1010,61 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
return -ENOMEM;
rc = tpm2_load_null(chip, &null_key);
if (rc)
goto out;
if (rc) {
kfree_sensitive(auth);
return rc;
}
auth->session = TPM_HEADER_SIZE;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
if (rc)
goto out;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf) {
kfree_sensitive(auth);
return -ENOMEM;
}
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
/* salt key handle */
tpm_buf_append_u32(&buf, null_key);
tpm_buf_append_u32(buf, null_key);
/* bind key handle */
tpm_buf_append_u32(&buf, TPM2_RH_NULL);
tpm_buf_append_u32(buf, TPM2_RH_NULL);
/* nonce caller */
get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
tpm_buf_append_u16(buf, sizeof(auth->our_nonce));
tpm_buf_append(buf, auth->our_nonce, sizeof(auth->our_nonce));
/* append encrypted salt and squirrel away unencrypted in auth */
rc = tpm_buf_append_salt(&buf, chip, auth);
rc = tpm_buf_append_salt(buf, chip, auth);
if (rc) {
tpm2_flush_context(chip, null_key);
tpm_buf_destroy(&buf);
goto out;
kfree_sensitive(auth);
return rc;
}
/* session type (HMAC, audit or policy) */
tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
tpm_buf_append_u8(buf, TPM2_SE_HMAC);
/* symmetric encryption parameters */
/* symmetric algorithm */
tpm_buf_append_u16(&buf, TPM_ALG_AES);
tpm_buf_append_u16(buf, TPM_ALG_AES);
/* bits for symmetric algorithm */
tpm_buf_append_u16(&buf, AES_KEY_BITS);
tpm_buf_append_u16(buf, AES_KEY_BITS);
/* symmetric algorithm mode (must be CFB) */
tpm_buf_append_u16(&buf, TPM_ALG_CFB);
tpm_buf_append_u16(buf, TPM_ALG_CFB);
/* hash algorithm for session */
tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
tpm_buf_append_u16(buf, TPM_ALG_SHA256);
rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
rc = tpm_ret_to_err(tpm_transmit_cmd(chip, buf, 0, "StartAuthSession"));
tpm2_flush_context(chip, null_key);
if (rc == TPM2_RC_SUCCESS)
rc = tpm2_parse_start_auth_session(auth, &buf);
tpm_buf_destroy(&buf);
rc = tpm2_parse_start_auth_session(auth, buf);
if (rc == TPM2_RC_SUCCESS) {
chip->auth = auth;
return 0;
}
out:
kfree_sensitive(auth);
return rc;
}
@ -1285,19 +1278,21 @@ static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
u32 *handle, u8 *name)
{
struct tpm_buf *template __free(kfree) = NULL;
struct tpm_buf *buf __free(kfree) = NULL;
int rc;
struct tpm_buf buf;
struct tpm_buf template;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
if (rc)
return rc;
buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = tpm_buf_init_sized(&template);
if (rc) {
tpm_buf_destroy(&buf);
return rc;
}
template = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!template)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
tpm_buf_init_sized(template, TPM_BUFSIZE);
/*
* create the template. Note: in order for userspace to
@ -1309,75 +1304,72 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
*/
/* key type */
tpm_buf_append_u16(&template, TPM_ALG_ECC);
tpm_buf_append_u16(template, TPM_ALG_ECC);
/* name algorithm */
tpm_buf_append_u16(&template, TPM_ALG_SHA256);
tpm_buf_append_u16(template, TPM_ALG_SHA256);
/* object properties */
tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
tpm_buf_append_u32(template, TPM2_OA_NULL_KEY);
/* sauth policy (empty) */
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u16(template, 0);
/* BEGIN parameters: key specific; for ECC*/
/* symmetric algorithm */
tpm_buf_append_u16(&template, TPM_ALG_AES);
tpm_buf_append_u16(template, TPM_ALG_AES);
/* bits for symmetric algorithm */
tpm_buf_append_u16(&template, AES_KEY_BITS);
tpm_buf_append_u16(template, AES_KEY_BITS);
/* algorithm mode (must be CFB) */
tpm_buf_append_u16(&template, TPM_ALG_CFB);
tpm_buf_append_u16(template, TPM_ALG_CFB);
/* scheme (NULL means any scheme) */
tpm_buf_append_u16(&template, TPM_ALG_NULL);
tpm_buf_append_u16(template, TPM_ALG_NULL);
/* ECC Curve ID */
tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
tpm_buf_append_u16(template, TPM2_ECC_NIST_P256);
/* KDF Scheme */
tpm_buf_append_u16(&template, TPM_ALG_NULL);
tpm_buf_append_u16(template, TPM_ALG_NULL);
/* unique: key specific; for ECC it is two zero size points */
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u16(template, 0);
tpm_buf_append_u16(template, 0);
/* END parameters */
/* primary handle */
tpm_buf_append_u32(&buf, hierarchy);
tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
tpm_buf_append_u32(buf, hierarchy);
tpm_buf_append_empty_auth(buf, TPM2_RS_PW);
/* sensitive create size is 4 for two empty buffers */
tpm_buf_append_u16(&buf, 4);
tpm_buf_append_u16(buf, 4);
/* sensitive create auth data (empty) */
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u16(buf, 0);
/* sensitive create sensitive data (empty) */
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u16(buf, 0);
/* the public template */
tpm_buf_append(&buf, template.data, template.length);
tpm_buf_destroy(&template);
tpm_buf_append(buf, template->data, template->length);
/* outside info (empty) */
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u16(buf, 0);
/* creation PCR (none) */
tpm_buf_append_u32(&buf, 0);
tpm_buf_append_u32(buf, 0);
rc = tpm_transmit_cmd(chip, &buf, 0,
rc = tpm_transmit_cmd(chip, buf, 0,
"attempting to create NULL primary");
if (rc == TPM2_RC_SUCCESS)
rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
rc = tpm2_parse_create_primary(chip, buf, handle, hierarchy,
name);
tpm_buf_destroy(&buf);
return rc;
}

View File

@ -15,19 +15,6 @@
#include <linux/unaligned.h>
#include "tpm.h"
enum tpm2_handle_types {
TPM2_HT_HMAC_SESSION = 0x02000000,
TPM2_HT_POLICY_SESSION = 0x03000000,
TPM2_HT_TRANSIENT = 0x80000000,
};
struct tpm2_context {
__be64 sequence;
__be32 saved_handle;
__be32 hierarchy;
__be16 blob_size;
} __packed;
static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
{
int i;
@ -71,24 +58,25 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
unsigned int *offset, u32 *handle)
{
struct tpm_buf tbuf;
struct tpm2_context *ctx;
unsigned int body_size;
int rc;
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
if (rc)
return rc;
struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!tbuf)
return -ENOMEM;
tpm_buf_init(tbuf, TPM_BUFSIZE);
tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
ctx = (struct tpm2_context *)&buf[*offset];
body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
tpm_buf_append(&tbuf, &buf[*offset], body_size);
tpm_buf_append(tbuf, &buf[*offset], body_size);
rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
rc = tpm_transmit_cmd(chip, tbuf, 4, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
tpm_buf_destroy(&tbuf);
return -EFAULT;
} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
rc == TPM2_RC_REFERENCE_H0) {
@ -103,64 +91,55 @@ int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
* flushed outside the space
*/
*handle = 0;
tpm_buf_destroy(&tbuf);
return -ENOENT;
} else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
tpm_buf_destroy(&tbuf);
return -EINVAL;
} else if (rc > 0) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
tpm_buf_destroy(&tbuf);
return -EFAULT;
}
*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
*handle = be32_to_cpup((__be32 *)&tbuf->data[TPM_HEADER_SIZE]);
*offset += body_size;
tpm_buf_destroy(&tbuf);
return 0;
}
int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
unsigned int buf_size, unsigned int *offset)
{
struct tpm_buf tbuf;
unsigned int body_size;
int rc;
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
if (rc)
return rc;
struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!tbuf)
return -ENOMEM;
tpm_buf_append_u32(&tbuf, handle);
tpm_buf_init(tbuf, TPM_BUFSIZE);
tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
tpm_buf_append_u32(tbuf, handle);
rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
rc = tpm_transmit_cmd(chip, tbuf, 0, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
tpm_buf_destroy(&tbuf);
return -EFAULT;
} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
tpm_buf_destroy(&tbuf);
return -ENOENT;
} else if (rc) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
tpm_buf_destroy(&tbuf);
return -EFAULT;
}
body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
body_size = tpm_buf_length(tbuf) - TPM_HEADER_SIZE;
if ((*offset + body_size) > buf_size) {
dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
tpm_buf_destroy(&tbuf);
return -ENOMEM;
}
memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
memcpy(&buf[*offset], &tbuf->data[TPM_HEADER_SIZE], body_size);
*offset += body_size;
tpm_buf_destroy(&tbuf);
return 0;
}

View File

@ -182,8 +182,10 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
timeout);
if (rc > 0)
return 0;
/* At this point we know that the SINT pin is asserted, so we
* do not need to do i2c_nuvoton_check_status */
disable_irq(priv->irq);
if (rc < 0)
return rc;
} else {
unsigned long ten_msec, stop;
bool status_valid;

View File

@ -19,71 +19,9 @@
#ifndef __TPM_TIS_CORE_H__
#define __TPM_TIS_CORE_H__
#include <linux/tpm_ptp.h>
#include "tpm.h"
enum tis_access {
TPM_ACCESS_VALID = 0x80,
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
TPM_ACCESS_REQUEST_PENDING = 0x04,
TPM_ACCESS_REQUEST_USE = 0x02,
};
enum tis_status {
TPM_STS_VALID = 0x80,
TPM_STS_COMMAND_READY = 0x40,
TPM_STS_GO = 0x20,
TPM_STS_DATA_AVAIL = 0x10,
TPM_STS_DATA_EXPECT = 0x08,
TPM_STS_RESPONSE_RETRY = 0x02,
TPM_STS_READ_ZERO = 0x23, /* bits that must be zero on read */
};
enum tis_int_flags {
TPM_GLOBAL_INT_ENABLE = 0x80000000,
TPM_INTF_BURST_COUNT_STATIC = 0x100,
TPM_INTF_CMD_READY_INT = 0x080,
TPM_INTF_INT_EDGE_FALLING = 0x040,
TPM_INTF_INT_EDGE_RISING = 0x020,
TPM_INTF_INT_LEVEL_LOW = 0x010,
TPM_INTF_INT_LEVEL_HIGH = 0x008,
TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
TPM_INTF_STS_VALID_INT = 0x002,
TPM_INTF_DATA_AVAIL_INT = 0x001,
};
enum tis_defaults {
TIS_MEM_LEN = 0x5000,
TIS_SHORT_TIMEOUT = 750, /* ms */
TIS_LONG_TIMEOUT = 4000, /* 4 secs */
TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */
TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */
};
/* Some timeout values are needed before it is known whether the chip is
* TPM 1.0 or TPM 2.0.
*/
#define TIS_TIMEOUT_A_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
#define TIS_TIMEOUT_B_MAX max_t(int, TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
#define TIS_TIMEOUT_C_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
#define TIS_TIMEOUT_D_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
#define TPM_STS(l) (0x0018 | ((l) << 12))
#define TPM_STS3(l) (0x001b | ((l) << 12))
#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
#define TPM_RID(l) (0x0F04 | ((l) << 12))
#define LPC_CNTRL_OFFSET 0x84
#define LPC_CLKRUN_EN (1 << 2)
#define INTEL_LEGACY_BLK_BASE_ADDR 0xFED08000
#define ILB_REMAP_SIZE 0x100
enum tpm_tis_flags {
TPM_TIS_ITPM_WORKAROUND = 0,
TPM_TIS_INVALID_STATUS = 1,

View File

@ -40,8 +40,12 @@ static inline int cr50_spi_probe(struct spi_device *spi)
}
#endif
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_TCG_TIS_SPI_CR50)
extern int tpm_tis_spi_resume(struct device *dev);
#if defined(CONFIG_PM_SLEEP)
#if defined(CONFIG_TCG_TIS_SPI_CR50)
extern int tpm_tis_spi_resume(struct device *dev);
#else
#define tpm_tis_spi_resume tpm_tis_resume
#endif
#else
#define tpm_tis_spi_resume NULL
#endif

View File

@ -395,40 +395,36 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
{
struct tpm_buf buf;
int rc;
const struct tpm_header *header;
struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
TPM2_CC_SET_LOCALITY);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
else
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
TPM_ORD_SET_LOCALITY);
if (rc)
return rc;
tpm_buf_append_u8(&buf, locality);
tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
tpm_buf_append_u8(buf, locality);
proxy_dev->state |= STATE_DRIVER_COMMAND;
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
rc = tpm_transmit_cmd(chip, buf, 0, "attempting to set locality");
proxy_dev->state &= ~STATE_DRIVER_COMMAND;
if (rc < 0) {
locality = rc;
goto out;
}
if (rc < 0)
return rc;
header = (const struct tpm_header *)buf.data;
header = (const struct tpm_header *)buf->data;
rc = be32_to_cpu(header->return_code);
if (rc)
locality = -1;
out:
tpm_buf_destroy(&buf);
return locality;
}

View File

@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request UE IRQ %d (%d)\n",
al_mc->irq_ue, ret);
if (ret != 0)
return ret;
}
}
if (al_mc->irq_ce > 0) {
@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request CE IRQ %d (%d)\n",
al_mc->irq_ce, ret);
if (ret != 0)
return ret;
}
}
return 0;

View File

@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
int edac_idx, rc;
struct device_node *np;
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
bool is_s10 = device->edac->is_s10;
rc = altr_check_ecc_deps(device);
if (rc)
@ -1548,15 +1549,14 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
/*
* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly
*
* FIXME: Instead of ifdefs with different architectures the driver
* should properly use compatibles.
*/
#ifdef CONFIG_64BIT
altdev->sb_irq = irq_of_parse_and_map(np, 1);
#else
altdev->sb_irq = irq_of_parse_and_map(np, 2);
#endif
/* Using compatibles to determine the IRQ Index */
if (is_s10)
altdev->sb_irq = irq_of_parse_and_map(np, 1);
else
altdev->sb_irq = irq_of_parse_and_map(np, 2);
if (!altdev->sb_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
rc = -ENODEV;
@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
goto err_release_group_1;
}
#ifdef CONFIG_64BIT
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Error PortB DBIRQ alloc\n");
goto err_release_group_1;
if (is_s10) {
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
goto err_release_group_1;
}
} else {
altdev->db_irq = irq_of_parse_and_map(np, 3);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
goto err_release_group_1;
}
}
#else
altdev->db_irq = irq_of_parse_and_map(np, 3);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
goto err_release_group_1;
}
#endif
rc = edac_device_add_device(dci);
if (rc) {
@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
goto err_release_group1;
}
#ifdef CONFIG_64BIT
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to parse DB IRQ index\n");
goto err_release_group1;
if (edac->is_s10) {
/* Use IRQ to determine SError origin instead of assigning IRQ */
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"Unable to parse DB IRQ index\n");
goto err_release_group1;
}
} else {
altdev->db_irq = irq_of_parse_and_map(np, 1);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
rc = -ENODEV;
goto err_release_group1;
}
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
goto err_release_group1;
}
}
#else
altdev->db_irq = irq_of_parse_and_map(np, 1);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
rc = -ENODEV;
goto err_release_group1;
}
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
IRQF_TRIGGER_HIGH,
ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
goto err_release_group1;
}
#endif
rc = edac_device_add_device(dci);
if (rc) {
@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
/************** Stratix 10 EDAC Double Bit Error Handler ************/
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)
#ifdef CONFIG_64BIT
/* panic routine issues reboot on non-zero panic_timeout */
extern int panic_timeout;
@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this,
return NOTIFY_DONE;
}
#endif
/****************** Arria 10 EDAC Probe Function *********************/
static int altr_edac_a10_probe(struct platform_device *pdev)
@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->a10_ecc_devices);
edac->is_s10 = !!device_get_match_data(&pdev->dev);
edac->ecc_mgr_map =
altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node,
"altr,sysmgr-syscon");
@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
irq_set_chained_handler_and_data(edac->sb_irq,
altr_edac_a10_irq_handler,
edac);
#ifdef CONFIG_64BIT
if (edac->is_s10)
{
int dberror, err_addr;
@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
regmap_write(edac->ecc_mgr_map,
S10_SYSMGR_UE_ADDR_OFST, 0);
}
}
#else
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0)
return edac->db_irq;
} else {
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0)
return edac->db_irq;
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler, edac);
#endif
irq_set_chained_handler_and_data(edac->db_irq,
altr_edac_a10_irq_handler, edac);
}
for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
static const struct of_device_id altr_edac_a10_of_match[] = {
{ .compatible = "altr,socfpga-a10-ecc-manager" },
{ .compatible = "altr,socfpga-s10-ecc-manager" },
{ .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);

View File

@ -394,6 +394,7 @@ struct altr_arria10_edac {
struct irq_chip irq_chip;
struct list_head a10_ecc_devices;
struct notifier_block panic_notifier;
bool is_s10;
};
#endif /* #ifndef _ALTERA_EDAC_H */

View File

@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void)
goto err_pci;
}
request_module_nowait("amd_atl");
/* register stuff with EDAC MCE */
if (boot_cpu_data.x86 >= 0x17) {
amd_register_ecc_decoder(decode_umc_error);

View File

@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
DRV_NAME, ctx);
if (rc) {
dev_err(&pdev->dev, "unable to request irq %d\n", irq);
if (rc)
return rc;
}
/* enable interrupts */
regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,

View File

@ -1,50 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/string_choices.h>
#include "edac_module.h"
static struct dentry *edac_debugfs;
static ssize_t edac_fake_inject_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct device *dev = file->private_data;
struct mem_ctl_info *mci = to_mci(dev);
static enum hw_event_mc_err_type type;
u16 errcount = mci->fake_inject_count;
if (!errcount)
errcount = 1;
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
: HW_EVENT_ERR_CORRECTED;
printk(KERN_DEBUG
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
errcount,
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
str_plural(errcount),
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2]
);
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
mci->fake_inject_layer[0],
mci->fake_inject_layer[1],
mci->fake_inject_layer[2],
"FAKE ERROR", "for EDAC testing only");
return count;
}
static const struct file_operations debug_fake_inject_fops = {
.open = simple_open,
.write = edac_fake_inject_write,
.llseek = generic_file_llseek,
};
void __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
@ -57,29 +16,7 @@ void edac_debugfs_exit(void)
void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
{
struct dentry *parent;
char name[80];
int i;
parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
for (i = 0; i < mci->n_layers; i++) {
sprintf(name, "fake_inject_%s",
edac_layer_name[mci->layers[i].type]);
debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_layer[i]);
}
debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_ue);
debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
&mci->fake_inject_count);
debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
&debug_fake_inject_fops);
mci->debugfs = parent;
mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
}
/* Create a toplevel dir under EDAC's debugfs hierarchy */

View File

@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
}
/*
* edac_device_reset_delay_period
*
* need to stop any outstanding workq queued up at this time
* because we will be resetting the sleep time.
* Then restart the workq on the new delay
* Stop any outstanding workq queued up at this time because sleep time will
* be reset. Then restart the workq on the new delay.
*/
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
unsigned long msec)
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec)
{
edac_dev->poll_msec = msec;
edac_dev->delay = msecs_to_jiffies(msec);

View File

@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
return sprintf(data, "%u\n", ctl_info->poll_msec);
}
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
*ctl_info, const char *data,
size_t count)
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info,
const char *data, size_t count)
{
unsigned long value;
unsigned int value;
int ret;
/* get the value and enforce that it is non-zero, must be at least
* one millisecond for the delay period, between scans
* Then cancel last outstanding delay for the work request
* and set a new one.
/*
* Get the value, make sure it is non-zero, must be at least one millisecond
* for the delay period between scans.
*/
value = simple_strtoul(data, NULL, 0);
ret = kstrtouint(data, 0, &value);
if (ret < 0)
return ret;
if (value < 1)
return -EINVAL;
edac_device_reset_delay_period(ctl_info, value);
return count;

View File

@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev,
ssize_t count;
count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
count += scnprintf(data + count, PAGE_SIZE - count, "\n");
count += sysfs_emit_at(data, count, "\n");
return count;
}

View File

@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay);
bool edac_stop_work(struct delayed_work *work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
*edac_dev, unsigned long msec);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec);
extern void edac_mc_reset_delay_period(unsigned long value);
/*

View File

@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
0, dev_name(&pdev->dev), mci);
if (res < 0) {
dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
if (res < 0)
goto err2;
}
devres_close_group(&pdev->dev, NULL);
return 0;

View File

@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm,
{
dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity;
dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1;
dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8;
switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) {
case 0:
dd->dtype = DEV_X8;
break;
case 1:
dd->dtype = DEV_X16;
break;
case 2:
dd->dtype = DEV_X32;
break;
case 3:
dd->dtype = DEV_X64;
break;
default:
dd->dtype = DEV_UNKNOWN;
}
}
static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window,

View File

@ -42,7 +42,8 @@
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
#define NUM_IMC 2 /* Max memory controllers */
/* Probing upper bound, not a hardware capability limit. */
#define MAX_IMC_TO_PROBE 8
#define NUM_CHANNELS 2 /* Max channels */
#define NUM_DIMMS 2 /* Max DIMMs per channel */
@ -122,6 +123,43 @@
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
/*
* A slice represents a portion of memory space participating in an
* interleave relationship within the memory hierarchy.
*
* It can represent in different levels such as:
*
* - a pair of memory controllers
* - a memory controller
* - a memory channel
* - a memory sub-channel / DIMM
*
* +--------+
* | |
* | Zone 1 |
* | |
* +--------+ +--------+
* | | | |
* | | | |
* | Zone 0 | | Zone 0 |
* | | | |
* | | | |
* +--------+ +--------+
*
* Slice L Slice S
*
* Memory space is divided into:
*
* - Zone 0 : Interleaved region
* - Zone 1 : Non-interleaved region (upper part of the large slice).
*/
struct slice {
/* Slice address. */
u64 addr;
/* Slice that @addr belongs to. */
int id;
};
struct igen6_imc {
int mc;
struct mem_ctl_info *mci;
@ -175,8 +213,6 @@ static struct res_config {
/* Set imc->dimm_{l_size,s_size,l_map}[chan]. */
void (*set_dimm_params)(struct igen6_imc *imc, int chan);
bool (*ibecc_available)(struct pci_dev *pdev);
/* Extract error address logged in IBECC */
u64 (*err_addr)(u64 ecclog);
/* Convert error address logged in IBECC to system physical address */
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
/* Convert error address logged in IBECC to integrated memory controller address */
@ -184,11 +220,11 @@ static struct res_config {
} *res_cfg;
static struct igen6_pvt {
struct igen6_imc imc[NUM_IMC];
void __iomem *memss_pma_cr;
u64 ms_hash;
u64 ms_s_size;
int ms_l_map;
struct igen6_imc imc[];
} *igen6_pvt;
/* The top of low usable DRAM */
@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE];
static struct irq_work ecclog_irq_work;
static struct work_struct ecclog_work;
/* Compute die IDs for Elkhart Lake with IBECC */
/* SoC compute die IDs with IBECC capability. */
/* Elkhart Lake */
#define DID_EHL_SKU5 0x4514
#define DID_EHL_SKU6 0x4528
#define DID_EHL_SKU7 0x452a
@ -238,22 +275,22 @@ static struct work_struct ecclog_work;
#define DID_EHL_SKU14 0x4534
#define DID_EHL_SKU15 0x4536
/* Compute die IDs for ICL-NNPI with IBECC */
/* ICL-NNPI */
#define DID_ICL_SKU8 0x4581
#define DID_ICL_SKU10 0x4585
#define DID_ICL_SKU11 0x4589
#define DID_ICL_SKU12 0x458d
/* Compute die IDs for Tiger Lake with IBECC */
/* Tiger Lake */
#define DID_TGL_SKU 0x9a14
/* Compute die IDs for Alder Lake with IBECC */
/* Alder Lake */
#define DID_ADL_SKU1 0x4601
#define DID_ADL_SKU2 0x4602
#define DID_ADL_SKU3 0x4621
#define DID_ADL_SKU4 0x4641
/* Compute die IDs for Alder Lake-N with IBECC */
/* Alder Lake-N */
#define DID_ADL_N_SKU1 0x4614
#define DID_ADL_N_SKU2 0x4617
#define DID_ADL_N_SKU3 0x461b
@ -267,38 +304,38 @@ static struct work_struct ecclog_work;
#define DID_ADL_N_SKU11 0x467c
#define DID_ADL_N_SKU12 0x4632
/* Compute die IDs for Arizona Beach with IBECC */
/* Arizona Beach */
#define DID_AZB_SKU1 0x4676
/* Compute did IDs for Amston Lake with IBECC */
/* Amston Lake */
#define DID_ASL_SKU1 0x464a
#define DID_ASL_SKU2 0x4646
#define DID_ASL_SKU3 0x4652
/* Compute die IDs for Raptor Lake-P with IBECC */
/* Raptor Lake-P */
#define DID_RPL_P_SKU1 0xa706
#define DID_RPL_P_SKU2 0xa707
#define DID_RPL_P_SKU3 0xa708
#define DID_RPL_P_SKU4 0xa716
#define DID_RPL_P_SKU5 0xa718
/* Compute die IDs for Meteor Lake-PS with IBECC */
/* Meteor Lake-PS */
#define DID_MTL_PS_SKU1 0x7d21
#define DID_MTL_PS_SKU2 0x7d22
#define DID_MTL_PS_SKU3 0x7d23
#define DID_MTL_PS_SKU4 0x7d24
/* Compute die IDs for Meteor Lake-P with IBECC */
/* Meteor Lake-P */
#define DID_MTL_P_SKU1 0x7d01
#define DID_MTL_P_SKU2 0x7d02
#define DID_MTL_P_SKU3 0x7d14
/* Compute die IDs for Arrow Lake-UH with IBECC */
/* Arrow Lake-UH */
#define DID_ARL_UH_SKU1 0x7d06
#define DID_ARL_UH_SKU2 0x7d20
#define DID_ARL_UH_SKU3 0x7d30
/* Compute die IDs for Panther Lake-H with IBECC */
/* Panther Lake-H */
#define DID_PTL_H_SKU1 0xb000
#define DID_PTL_H_SKU2 0xb001
#define DID_PTL_H_SKU3 0xb002
@ -314,15 +351,114 @@ static struct work_struct ecclog_work;
#define DID_PTL_H_SKU13 0xb02a
#define DID_PTL_H_SKU14 0xb00a
/* Compute die IDs for Wildcat Lake with IBECC */
/* Starfire */
#define DID_STF_SKU1 0xb02b
/* Wildcat Lake */
#define DID_WCL_SKU1 0xfd00
/* Compute die IDs for Nova Lake-H/HX with IBECC */
/* Nova Lake-H/HX */
#define DID_NVL_H_SKU1 0xd701
#define DID_NVL_H_SKU2 0xd702
#define DID_NVL_H_SKU3 0xd704
#define DID_NVL_H_SKU4 0xd705
/* Remove the interleave bit and shift upper part down to fill gap. */
static u64 squeeze_addr(u64 addr, int intlv_bit)
{
u64 slice_addr;
slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return slice_addr;
}
/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */
static u64 inflate_addr(u64 addr, int intlv_bit)
{
u64 inflated_addr;
/* Insert a zero at @intlv_bit position. */
inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1);
inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return inflated_addr;
}
static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit)
{
u64 hash_addr;
int i;
/*
* In hash mode, @intlv_bit is the lowest selected bit of @addr
* to be XORed. While @mask may or may not include this @intlv_bit,
* we enforce that @mask includes @intlv_bit to ensure @intlv_bit is
* XORed exactly once.
*/
hash_mask |= BIT_ULL(intlv_bit);
hash_addr = addr & hash_mask;
for (i = 6; i < 20; i++)
hash_base ^= (hash_addr >> i) & 1;
return hash_base;
}
/*
* Converts a higher-level address (system / IMC / channel) into a lower-level
* slice address and identifier.
*/
static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base,
int intlv_bit, u64 s_size, int l_map,
struct slice *slice)
{
/* In non-interleave zone. */
if (addr >= 2 * s_size) {
slice->addr = addr - s_size;
slice->id = l_map;
return;
}
/* In interleave zone. */
slice->addr = squeeze_addr(addr, intlv_bit);
/* Non-hash mode. */
if (!hash_mask) {
slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit);
return;
}
/* Hash mode. */
slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit);
}
/* Reconstruct address for upper memory hierarchy level. */
static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base,
int intlv_bit, u64 s_size)
{
u64 inflated_addr, hash_val;
/* In non-interleave zone. */
if (addr >= s_size)
return addr + s_size;
/*
* In interleave zone.
*
* Insert a zero at @intlv_bit position.
*/
inflated_addr = inflate_addr(addr, intlv_bit);
/*
* Reconstruct the removed interleave bit and use it to replace
* the zero at @intlv_bit position.
*/
hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit);
return inflated_addr | (hash_val << intlv_bit);
}
static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
{
union {
@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
return 0;
}
/* Check whether the memory controller is absent. */
static bool imc_absent(void __iomem *window)
{
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
}
/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
static void __iomem *map_imc_window(u64 mchbar, int pmc)
{
void __iomem *window;
window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
if (!window)
return NULL;
if (imc_absent(window)) {
iounmap(window);
return NULL;
}
return window;
}
/* Return the number of present memory controllers. */
static int get_imc_num(u64 mchbar)
{
void __iomem *window;
int lmc, pmc;
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
window = map_imc_window(mchbar, pmc);
if (window) {
iounmap(window);
lmc++;
}
}
return lmc;
}
static bool ehl_ibecc_available(struct pci_dev *pdev)
{
u32 v;
@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr)
return maddr;
}
static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit)
{
u64 hash_addr = addr & mask, hash = hash_init;
u64 intlv = (addr >> intlv_bit) & 1;
int i;
for (i = 6; i < 20; i++)
hash ^= (hash_addr >> i) & 1;
return hash ^ intlv;
}
static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
{
u64 maddr, hash, mask, ms_s_size;
u64 mask, ms_s_size;
int intlv_bit;
u32 ms_hash;
@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
mask = MEM_SLICE_HASH_MASK(ms_hash);
intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6;
maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) |
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
hash = mem_slice_hash(maddr, mask, mc, intlv_bit);
return maddr | (hash << intlv_bit);
return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size);
}
static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc)
@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc)
static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
{
u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size;
u64 ms_s_size = igen6_pvt->ms_s_size;
struct igen6_imc *imc = &igen6_pvt->imc[mc];
struct slice slice;
int intlv_bit;
u32 mc_hash;
@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6;
imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit |
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
return imc_addr;
}
static u64 rpl_p_err_addr(u64 ecclog)
{
return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog);
translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice);
return slice.addr;
}
static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc)
@ -686,6 +839,7 @@ static struct res_config tgl_cfg = {
.err_addr_to_imc_addr = tgl_err_addr_to_imc_addr,
};
/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */
static struct res_config adl_cfg = {
.machine_check = true,
.num_imc = 2,
@ -701,37 +855,6 @@ static struct res_config adl_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config adl_n_cfg = {
.machine_check = true,
.num_imc = 1,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x68,
.ibecc_available = tgl_ibecc_available,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config rpl_p_cfg = {
.machine_check = true,
.num_imc = 2,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x68,
.ibecc_available = tgl_ibecc_available,
.err_addr = rpl_p_err_addr,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config mtl_ps_cfg = {
.machine_check = true,
.num_imc = 2,
@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */
static struct res_config mtl_p_cfg = {
.machine_check = true,
.num_imc = 2,
@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
/* Shared by Panther Lake-H and Starfire */
static struct res_config ptl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config wcl_cfg = {
.machine_check = true,
.num_imc = 1,
.reg_mchbar_mask = GENMASK_ULL(41, 17),
.reg_tom_mask = GENMASK_ULL(41, 20),
.reg_touud_mask = GENMASK_ULL(41, 20),
.reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5),
.imc_base = 0xd800,
.ibecc_base = 0xd400,
.ibecc_error_log_offset = 0x170,
.ibecc_available = mtl_p_ibecc_available,
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
static struct res_config nvl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg },
{ PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan)
imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val);
}
static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit)
{
u64 hash_addr = addr & mask, hash = 0;
u64 intlv = (addr >> intlv_bit) & 1;
int i;
for (i = 6; i < 20; i++)
hash ^= (hash_addr >> i) & 1;
return (int)hash ^ intlv;
}
static u64 decode_channel_addr(u64 addr, int intlv_bit)
{
u64 channel_addr;
/* Remove the interleave bit and shift upper part down to fill gap */
channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
return channel_addr;
}
static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,
int *idx, u64 *sub_addr)
{
int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
if (addr > 2 * s_size) {
*sub_addr = addr - s_size;
*idx = l_map;
return;
}
if (CHANNEL_HASH_MODE(hash)) {
*sub_addr = decode_channel_addr(addr, intlv_bit);
*idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit);
} else {
*sub_addr = decode_channel_addr(addr, 6);
*idx = GET_BITFIELD(addr, 6, 6);
}
}
static int igen6_decode(struct decoded_addr *res)
{
struct igen6_imc *imc = &igen6_pvt->imc[res->mc];
u64 addr = res->imc_addr, sub_addr, s_size;
int idx, l_map;
u32 hash;
u64 addr = res->imc_addr, s_size;
int intlv_bit, l_map;
u32 hash, hash_mask;
struct slice slice;
if (addr >= igen6_tom) {
edac_dbg(0, "Address 0x%llx out of range\n", addr);
@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res)
hash = readl(imc->window + CHANNEL_HASH_OFFSET);
s_size = imc->ch_s_size;
l_map = imc->ch_l_map;
decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr);
res->channel_idx = idx;
res->channel_addr = sub_addr;
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
res->channel_idx = slice.id;
res->channel_addr = slice.addr;
/* Decode sub-channel/DIMM */
hash = readl(imc->window + CHANNEL_EHASH_OFFSET);
s_size = imc->dimm_s_size[idx];
l_map = imc->dimm_l_map[idx];
decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr);
res->sub_channel_idx = idx;
res->sub_channel_addr = sub_addr;
s_size = imc->dimm_s_size[res->channel_idx];
l_map = imc->dimm_l_map[res->channel_idx];
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
res->sub_channel_idx = slice.id;
res->sub_channel_addr = slice.addr;
return 0;
}
@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work)
llist_for_each_entry_safe(node, tmp, head, llnode) {
memset(&res, 0, sizeof(res));
if (res_cfg->err_addr)
eaddr = res_cfg->err_addr(node->ecclog);
else
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
res.mc = node->mc;
res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc);
res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc);
@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
{
void __iomem *memss_pma_cr;
struct igen6_pvt *pvt;
int imc_num, rc;
u64 mchbar;
int rc;
pvt = kzalloc_obj(*igen6_pvt);
if (!pvt)
return NULL;
rc = get_mchbar(pdev, &mchbar);
if (rc) {
kfree(pvt);
if (rc)
return NULL;
imc_num = get_imc_num(mchbar);
if (!imc_num) {
igen6_printk(KERN_ERR, "No mc found.\n");
return NULL;
}
edac_dbg(2, "%d mcs found.\n", imc_num);
/* Use the runtime detected IMC count. */
if (res_cfg->num_imc != imc_num)
res_cfg->num_imc = imc_num;
pvt = kzalloc_flex(*pvt, imc, imc_num);
if (!pvt)
return NULL;
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
if (!memss_pma_cr) {
@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci)
irq_work_queue(&ecclog_irq_work);
}
/* Check whether the memory controller is absent. */
static bool igen6_imc_absent(void __iomem *window)
{
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
}
static void imc_release(struct device *dev)
{
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
{
void __iomem *window;
int lmc, pmc, rc;
u64 base;
for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
base = mchbar + pmc * MCHBAR_SIZE;
window = ioremap(base, MCHBAR_SIZE);
if (!window) {
igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
rc = -ENOMEM;
goto out_unregister_mcis;
}
if (igen6_imc_absent(window)) {
iounmap(window);
edac_dbg(2, "Skip absent mc%d\n", pmc);
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
window = map_imc_window(mchbar, pmc);
if (!window)
continue;
}
rc = igen6_register_mci(lmc, window, pdev);
if (rc)
goto out_iounmap;
goto err_unregister;
/* Done, if all present MCs are detected and registered. */
if (++lmc >= res_cfg->num_imc)
@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
return 0;
out_iounmap:
err_unregister:
iounmap(window);
out_unregister_mcis:
igen6_unregister_mcis();
return rc;

View File

@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev,
thunderx_lmc_err_isr,
thunderx_lmc_threaded_isr, 0,
"[EDAC] ThunderX LMC", mci);
if (ret) {
dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
if (ret)
goto err_free;
}
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));

View File

@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq,
xgene_edac_isr, IRQF_SHARED,
dev_name(&pdev->dev), edac);
if (rc) {
dev_err(&pdev->dev,
"Could not request IRQ %d\n", irq);
if (rc)
goto out_err;
}
}
}

View File

@ -807,6 +807,153 @@ static uint32_t kgd_gfx_v11_hqd_sdma_get_doorbell(struct amdgpu_device *adev,
return 0;
}
static void lock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_lock(&adev->srbm_mutex);
mutex_lock(&adev->grbm_idx_mutex);
}
static void unlock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_unlock(&adev->grbm_idx_mutex);
mutex_unlock(&adev->srbm_mutex);
}
/**
* get_wave_count: Read device registers to get number of waves in flight for
* a particular queue. The method also returns the doorbell offset associated
* with the queue.
*
* @adev: Handle of device whose registers are to be read
* @queue_idx: Index of queue in the queue-map bit-field
* @queue_cnt: Stores the wave count and doorbell offset for an active queue
* @inst: xcc's instance number on a multi-XCC setup
*/
static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
struct kfd_cu_occupancy *queue_cnt, uint32_t inst)
{
int pipe_idx;
int queue_slot;
unsigned int reg_val;
unsigned int wave_cnt;
/*
* Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID
* parameters to read out waves in flight. Get doorbell offset if there are
* non-zero waves in flight.
*/
pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe;
queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe;
soc21_grbm_select(adev, 1, pipe_idx, queue_slot, 0);
reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, 0,
regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot);
wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK;
if (wave_cnt != 0) {
queue_cnt->wave_cnt += wave_cnt;
queue_cnt->doorbell_off =
(RREG32_SOC15(GC, 0, regCP_HQD_PQ_DOORBELL_CONTROL) &
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
}
}
/**
* kgd_gfx_v11_get_cu_occupancy: Reads relevant registers associated with each
* shader engine and aggregates the number of waves that are in flight for the
* process whose pasid is provided as a parameter. The process could have ZERO
* or more queues running and submitting waves to compute units.
*
* @adev: Handle of device from which to get number of waves in flight
* @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset
* for comparison later.
* @max_waves_per_cu: Output parameter updated with maximum number of waves
* possible per Compute Unit
* @inst: xcc's instance number on a multi-XCC setup
*
* Note: It's possible that the device has too many queues (oversubscription)
* in which case a VMID could be remapped to a different PASID. This could lead
* to an inaccurate wave count. Following is a high-level sequence:
* Time T1: vmid = getVmid(); vmid is associated with Pasid P1
* Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2
* In the sequence above wave count obtained from time T1 will be incorrectly
* lost or added to total wave count.
*
* The registers that provide the waves in flight are:
*
* SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a
* queue is slotted, OFF if there is no queue. A process could have ZERO or
* more queues slotted and submitting waves to be run on compute units. Even
* when there is a queue it is possible there could be zero wave fronts, this
* can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem
* command
*
* For each bit that is ON from above:
*
* Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the
* number of waves that are in flight for the queue at specified index. The
* index ranges from 0 to 7.
*
* If non-zero waves are in flight, store the corresponding doorbell offset
* of the queue, along with the wave count.
*
* Determine if the queue belongs to the process by comparing the doorbell
* offset against the process's queues. If it matches, aggregate the wave
* count for the process.
*
* Reading registers referenced above involves programming GRBM appropriately
*/
static void kgd_gfx_v11_get_cu_occupancy(struct amdgpu_device *adev,
struct kfd_cu_occupancy *cu_occupancy,
int *max_waves_per_cu, uint32_t inst)
{
int qidx;
int se_idx;
int se_cnt;
int queue_map;
int max_queue_cnt;
DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES);
lock_spi_csq_mutexes(adev);
soc21_grbm_select(adev, 1, 0, 0, 0);
/*
* Iterate through the shader engines and arrays of the device
* to get number of waves in flight
*/
bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap,
AMDGPU_MAX_QUEUES);
max_queue_cnt = adev->gfx.mec.num_pipe_per_mec *
adev->gfx.mec.num_queue_per_pipe;
se_cnt = adev->gfx.config.max_shader_engines;
for (se_idx = 0; se_idx < se_cnt; se_idx++) {
amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst);
queue_map = RREG32_SOC15(GC, 0,
regSPI_CSQ_WF_ACTIVE_STATUS);
for (qidx = 0; qidx < max_queue_cnt; qidx++) {
/* Skip queues that are not associated with
* compute functions
*/
if (!test_bit(qidx, cp_queue_bitmap))
continue;
if (!(queue_map & (1 << qidx)))
continue;
/* Get number of waves in flight and aggregate them */
get_wave_count(adev, qidx, &cu_occupancy[qidx], inst);
}
}
amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst);
soc21_grbm_select(adev, 0, 0, 0, 0);
unlock_spi_csq_mutexes(adev);
/* Update the output parameters and return */
*max_waves_per_cu = adev->gfx.cu_info.simd_per_cu *
adev->gfx.cu_info.max_waves_per_simd;
}
const struct kfd2kgd_calls gfx_v11_kfd2kgd = {
.program_sh_mem_settings = program_sh_mem_settings_v11,
.set_pasid_vmid_mapping = set_pasid_vmid_mapping_v11,
@ -832,5 +979,6 @@ const struct kfd2kgd_calls gfx_v11_kfd2kgd = {
.clear_address_watch = kgd_gfx_v11_clear_address_watch,
.hqd_get_pq_addr = kgd_gfx_v11_hqd_get_pq_addr,
.hqd_reset = kgd_gfx_v11_hqd_reset,
.get_cu_occupancy = kgd_gfx_v11_get_cu_occupancy,
.hqd_sdma_get_doorbell = kgd_gfx_v11_hqd_sdma_get_doorbell
};

View File

@ -368,6 +368,153 @@ static uint32_t kgd_gfx_v12_hqd_sdma_get_doorbell(struct amdgpu_device *adev,
return 0;
}
static void lock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_lock(&adev->srbm_mutex);
mutex_lock(&adev->grbm_idx_mutex);
}
static void unlock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_unlock(&adev->grbm_idx_mutex);
mutex_unlock(&adev->srbm_mutex);
}
/**
* get_wave_count: Read device registers to get number of waves in flight for
* a particular queue. The method also returns the doorbell offset associated
* with the queue.
*
* @adev: Handle of device whose registers are to be read
* @queue_idx: Index of queue in the queue-map bit-field
* @queue_cnt: Stores the wave count and doorbell offset for an active queue
* @inst: xcc's instance number on a multi-XCC setup
*/
static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
struct kfd_cu_occupancy *queue_cnt, uint32_t inst)
{
int pipe_idx;
int queue_slot;
unsigned int reg_val;
unsigned int wave_cnt;
/*
* Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID
* parameters to read out waves in flight. Get doorbell offset if there are
* non-zero waves in flight.
*/
pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe;
queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe;
soc24_grbm_select(adev, 1, pipe_idx, queue_slot, 0);
reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, 0,
regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot);
wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK;
if (wave_cnt != 0) {
queue_cnt->wave_cnt += wave_cnt;
queue_cnt->doorbell_off =
(RREG32_SOC15(GC, 0, regCP_HQD_PQ_DOORBELL_CONTROL) &
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
}
}
/**
* kgd_gfx_v12_get_cu_occupancy: Reads relevant registers associated with each
* shader engine and aggregates the number of waves that are in flight for the
* process whose pasid is provided as a parameter. The process could have ZERO
* or more queues running and submitting waves to compute units.
*
* @adev: Handle of device from which to get number of waves in flight
* @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset
* for comparison later.
* @max_waves_per_cu: Output parameter updated with maximum number of waves
* possible per Compute Unit
* @inst: xcc's instance number on a multi-XCC setup
*
* Note: It's possible that the device has too many queues (oversubscription)
* in which case a VMID could be remapped to a different PASID. This could lead
* to an inaccurate wave count. Following is a high-level sequence:
* Time T1: vmid = getVmid(); vmid is associated with Pasid P1
* Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2
* In the sequence above wave count obtained from time T1 will be incorrectly
* lost or added to total wave count.
*
* The registers that provide the waves in flight are:
*
* SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a
* queue is slotted, OFF if there is no queue. A process could have ZERO or
* more queues slotted and submitting waves to be run on compute units. Even
* when there is a queue it is possible there could be zero wave fronts, this
* can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem
* command
*
* For each bit that is ON from above:
*
* Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the
* number of waves that are in flight for the queue at specified index. The
* index ranges from 0 to 7.
*
* If non-zero waves are in flight, store the corresponding doorbell offset
* of the queue, along with the wave count.
*
* Determine if the queue belongs to the process by comparing the doorbell
* offset against the process's queues. If it matches, aggregate the wave
* count for the process.
*
* Reading registers referenced above involves programming GRBM appropriately
*/
static void kgd_gfx_v12_get_cu_occupancy(struct amdgpu_device *adev,
struct kfd_cu_occupancy *cu_occupancy,
int *max_waves_per_cu, uint32_t inst)
{
int qidx;
int se_idx;
int se_cnt;
int queue_map;
int max_queue_cnt;
DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES);
lock_spi_csq_mutexes(adev);
soc24_grbm_select(adev, 1, 0, 0, 0);
/*
* Iterate through the shader engines and arrays of the device
* to get number of waves in flight
*/
bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap,
AMDGPU_MAX_QUEUES);
max_queue_cnt = adev->gfx.mec.num_pipe_per_mec *
adev->gfx.mec.num_queue_per_pipe;
se_cnt = adev->gfx.config.max_shader_engines;
for (se_idx = 0; se_idx < se_cnt; se_idx++) {
amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst);
queue_map = RREG32_SOC15(GC, 0,
regSPI_CSQ_WF_ACTIVE_STATUS);
for (qidx = 0; qidx < max_queue_cnt; qidx++) {
/* Skip queues that are not associated with
* compute functions
*/
if (!test_bit(qidx, cp_queue_bitmap))
continue;
if (!(queue_map & (1 << qidx)))
continue;
/* Get number of waves in flight and aggregate them */
get_wave_count(adev, qidx, &cu_occupancy[qidx], inst);
}
}
amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst);
soc24_grbm_select(adev, 0, 0, 0, 0);
unlock_spi_csq_mutexes(adev);
/* Update the output parameters and return */
*max_waves_per_cu = adev->gfx.cu_info.simd_per_cu *
adev->gfx.cu_info.max_waves_per_simd;
}
const struct kfd2kgd_calls gfx_v12_kfd2kgd = {
.init_interrupts = init_interrupts_v12,
.hqd_dump = hqd_dump_v12,
@ -381,5 +528,6 @@ const struct kfd2kgd_calls gfx_v12_kfd2kgd = {
.set_wave_launch_mode = kgd_gfx_v12_set_wave_launch_mode,
.set_address_watch = kgd_gfx_v12_set_address_watch,
.clear_address_watch = kgd_gfx_v12_clear_address_watch,
.hqd_sdma_get_doorbell = kgd_gfx_v12_hqd_sdma_get_doorbell
.hqd_sdma_get_doorbell = kgd_gfx_v12_hqd_sdma_get_doorbell,
.get_cu_occupancy = kgd_gfx_v12_get_cu_occupancy,
};

View File

@ -371,6 +371,153 @@ static uint32_t kgd_gfx_v12_1_hqd_sdma_get_doorbell(struct amdgpu_device *adev,
return 0;
}
static void lock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_lock(&adev->srbm_mutex);
mutex_lock(&adev->grbm_idx_mutex);
}
static void unlock_spi_csq_mutexes(struct amdgpu_device *adev)
{
mutex_unlock(&adev->grbm_idx_mutex);
mutex_unlock(&adev->srbm_mutex);
}
/**
* get_wave_count: Read device registers to get number of waves in flight for
* a particular queue. The method also returns the doorbell offset associated
* with the queue.
*
* @adev: Handle of device whose registers are to be read
* @queue_idx: Index of queue in the queue-map bit-field
* @queue_cnt: Stores the wave count and doorbell offset for an active queue
* @inst: xcc's instance number on a multi-XCC setup
*/
static void get_wave_count(struct amdgpu_device *adev, int queue_idx,
struct kfd_cu_occupancy *queue_cnt, uint32_t inst)
{
int pipe_idx;
int queue_slot;
unsigned int reg_val;
unsigned int wave_cnt;
/*
* Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID
* parameters to read out waves in flight. Get doorbell offset if there are
* non-zero waves in flight.
*/
pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe;
queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe;
amdgpu_gfx_select_me_pipe_q(adev, 1, pipe_idx, queue_slot, 0, inst);
reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, GET_INST(GC, inst),
regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot);
wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK;
if (wave_cnt != 0) {
queue_cnt->wave_cnt += wave_cnt;
queue_cnt->doorbell_off =
(RREG32_SOC15(GC, GET_INST(GC, inst), regCP_HQD_PQ_DOORBELL_CONTROL) &
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
}
}
/**
* kgd_gfx_v12_1_get_cu_occupancy: Reads relevant registers associated with
* each shader engine and aggregates the number of waves that are in flight
* for the process whose pasid is provided as a parameter. The process could
* have ZERO or more queues running and submitting waves to compute units.
*
* @adev: Handle of device from which to get number of waves in flight
* @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset
* for comparison later.
* @max_waves_per_cu: Output parameter updated with maximum number of waves
* possible per Compute Unit
* @inst: xcc's instance number on a multi-XCC setup
*
* Note: It's possible that the device has too many queues (oversubscription)
* in which case a VMID could be remapped to a different PASID. This could lead
* to an inaccurate wave count. Following is a high-level sequence:
* Time T1: vmid = getVmid(); vmid is associated with Pasid P1
* Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2
* In the sequence above wave count obtained from time T1 will be incorrectly
* lost or added to total wave count.
*
* The registers that provide the waves in flight are:
*
* SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a
* queue is slotted, OFF if there is no queue. A process could have ZERO or
* more queues slotted and submitting waves to be run on compute units. Even
* when there is a queue it is possible there could be zero wave fronts, this
* can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem
* command
*
* For each bit that is ON from above:
*
* Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the
* number of waves that are in flight for the queue at specified index. The
* index ranges from 0 to 7.
*
* If non-zero waves are in flight, store the corresponding doorbell offset
* of the queue, along with the wave count.
*
* Determine if the queue belongs to the process by comparing the doorbell
* offset against the process's queues. If it matches, aggregate the wave
* count for the process.
*
* Reading registers referenced above involves programming GRBM appropriately
*/
static void kgd_gfx_v12_1_get_cu_occupancy(struct amdgpu_device *adev,
struct kfd_cu_occupancy *cu_occupancy,
int *max_waves_per_cu, uint32_t inst)
{
int qidx;
int se_idx;
int se_cnt;
int queue_map;
int max_queue_cnt;
DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES);
lock_spi_csq_mutexes(adev);
amdgpu_gfx_select_me_pipe_q(adev, 1, 0, 0, 0, inst);
/*
* Iterate through the shader engines and arrays of the device
* to get number of waves in flight
*/
bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap,
AMDGPU_MAX_QUEUES);
max_queue_cnt = adev->gfx.mec.num_pipe_per_mec *
adev->gfx.mec.num_queue_per_pipe;
se_cnt = adev->gfx.config.max_shader_engines;
for (se_idx = 0; se_idx < se_cnt; se_idx++) {
amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst);
queue_map = RREG32_SOC15(GC, GET_INST(GC, inst),
regSPI_CSQ_WF_ACTIVE_STATUS);
for (qidx = 0; qidx < max_queue_cnt; qidx++) {
/* Skip queues that are not associated with
* compute functions
*/
if (!test_bit(qidx, cp_queue_bitmap))
continue;
if (!(queue_map & (1 << qidx)))
continue;
/* Get number of waves in flight and aggregate them */
get_wave_count(adev, qidx, &cu_occupancy[qidx], inst);
}
}
amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst);
amdgpu_gfx_select_me_pipe_q(adev, 0, 0, 0, 0, inst);
unlock_spi_csq_mutexes(adev);
/* Update the output parameters and return */
*max_waves_per_cu = adev->gfx.cu_info.simd_per_cu *
adev->gfx.cu_info.max_waves_per_simd;
}
const struct kfd2kgd_calls gfx_v12_1_kfd2kgd = {
.init_interrupts = init_interrupts_v12_1,
.hqd_dump = hqd_dump_v12_1,
@ -384,5 +531,6 @@ const struct kfd2kgd_calls gfx_v12_1_kfd2kgd = {
.set_wave_launch_mode = kgd_gfx_v12_1_set_wave_launch_mode,
.set_address_watch = kgd_gfx_v12_1_set_address_watch,
.clear_address_watch = kgd_gfx_v12_1_clear_address_watch,
.hqd_sdma_get_doorbell = kgd_gfx_v12_1_hqd_sdma_get_doorbell
.hqd_sdma_get_doorbell = kgd_gfx_v12_1_hqd_sdma_get_doorbell,
.get_cu_occupancy = kgd_gfx_v12_1_get_cu_occupancy
};

View File

@ -1795,6 +1795,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
size >>= 1;
aligned_size = PAGE_ALIGN(size);
/* reject AQL queue with size < 2 */
if (!aligned_size) {
ret = -EINVAL;
goto err_alignment_size;
}
(*mem)->alloc_flags = flags;
amdgpu_sync_create(&(*mem)->sync);
@ -1886,6 +1892,7 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id);
err_reserve_limit:
amdgpu_sync_free(&(*mem)->sync);
err_alignment_size:
mutex_destroy(&(*mem)->lock);
if (gobj)
drm_gem_object_put(gobj);

View File

@ -609,6 +609,13 @@ void amdgpu_device_detect_runtime_pm_mode(struct amdgpu_device *adev)
int bamaco_support;
adev->pm.rpm_mode = AMDGPU_RUNPM_NONE;
if (pci_is_thunderbolt_attached(adev->pdev) ||
dev_is_removable(&adev->pdev->dev)) {
dev_info(adev->dev,
"Runtime PM disabled for externally attached device\n");
return;
}
bamaco_support = amdgpu_device_supports_baco(adev);
switch (amdgpu_runtime_pm) {
@ -2523,7 +2530,11 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev)
if (r)
goto init_failed;
amdgpu_ttm_enable_buffer_funcs(adev);
/* If SDMA is not brought up during hwini, the ttm buffer funcs enablement
* is delayed after reset-on-init completes.
*/
if (amdgpu_ip_member_of_hwini(adev, AMD_IP_BLOCK_TYPE_SDMA))
amdgpu_ttm_enable_buffer_funcs(adev);
/* Don't init kfd if whole hive need to be reset during init */
if (adev->init_lvl->level != AMDGPU_INIT_LEVEL_MINIMAL_XGMI) {
@ -3733,6 +3744,14 @@ static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev)
amdgpu_ptl_sysfs_fini(adev);
}
static bool
amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px)
{
return !pci_is_thunderbolt_attached(adev->pdev) &&
(px || (!dev_is_removable(&adev->pdev->dev) &&
apple_gmux_detect(NULL, NULL)));
}
/**
* amdgpu_device_init - initialize the driver
*
@ -4183,8 +4202,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
px = amdgpu_device_supports_px(adev);
if (px || (!dev_is_removable(&adev->pdev->dev) &&
apple_gmux_detect(NULL, NULL)))
if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_register_client(adev->pdev,
&amdgpu_switcheroo_ops, px);
@ -4349,8 +4367,7 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
px = amdgpu_device_supports_px(adev);
if (px || (!dev_is_removable(&adev->pdev->dev) &&
apple_gmux_detect(NULL, NULL)))
if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_unregister_client(adev->pdev);
if (px)
@ -5037,6 +5054,31 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev,
amdgpu_fence_driver_force_completion(ring, fence);
}
/*
* MES scheduler rings have no drm scheduler, so they are missed by the
* loop above. Realign their polling fence too (one per XCC), otherwise the
* first post-reset submission polls forever on a stale seq. sched.ready is
* only set while the driver owns the ring.
*/
for (i = 0; i < AMDGPU_MAX_MES_INST_PIPES; i++) {
struct amdgpu_ring *mes_ring = &adev->mes.ring[i];
if (mes_ring->fence_drv.initialized && mes_ring->sched.ready)
amdgpu_fence_driver_force_completion(mes_ring, fence);
}
/*
* KIQ rings are polling-fence/no_scheduler like MES, so realign their
* fence too (one ring per XCC), otherwise the first post-reset KIQ
* submission polls forever on a stale seq.
*/
for (i = 0; i < AMDGPU_MAX_GC_INSTANCES; i++) {
struct amdgpu_ring *kiq_ring = &adev->gfx.kiq[i].ring;
if (kiq_ring->fence_drv.initialized && kiq_ring->sched.ready)
amdgpu_fence_driver_force_completion(kiq_ring, fence);
}
amdgpu_fence_driver_isr_toggle(adev, false);
r = amdgpu_reset_prepare_hwcontext(adev, reset_context);
@ -5137,8 +5179,6 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context)
if (r)
goto out;
amdgpu_ttm_enable_buffer_funcs(tmp_adev);
r = amdgpu_device_ip_resume_phase3(tmp_adev);
if (r)
goto out;
@ -6647,8 +6687,8 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev,
struct amdgpu_ring *ring,
struct amdgpu_job *job)
{
struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id];
struct drm_sched_fence *f = job->base.s_fence;
struct amdgpu_isolation *isolation;
struct dma_fence *dep;
void *owner;
int r;
@ -6661,6 +6701,9 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev,
ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE)
return NULL;
isolation = &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ?
0 : ring->xcp_id];
/*
* All submissions where enforce isolation is false are handled as if
* they come from a single client. Use ~0l as the owner to distinct it

View File

@ -1307,8 +1307,19 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
ip_hw_instance->num_instance);
ip_hw_instance->num_base_addresses = ip->num_base_address;
for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++)
ip_hw_instance->base_addr[kk] = ip->base_address[kk];
for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++) {
/*
* Standalone mode uses a raw copy of the discovery
* binary; decode 64-bit addresses here. The shared
* bin is already collapsed to 32-bit in place.
*/
if (reg_base_64 && ip_top->standalone_mode)
ip_hw_instance->base_addr[kk] =
lower_32_bits(le64_to_cpu(ip->base_address_64[kk])) & 0x3FFFFFFF;
else
ip_hw_instance->base_addr[kk] =
le32_to_cpu(ip->base_address[kk]);
}
kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype);
ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;

View File

@ -2557,6 +2557,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev);
pm_runtime_dont_use_autosuspend(dev->dev);
}
amdgpu_driver_unload_kms(dev);

Some files were not shown because too many files have changed in this diff Show More