mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
a5249d1d9a
7
.mailmap
7
.mailmap
|
|
@ -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
11
CREDITS
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
63
Documentation/devicetree/bindings/rtc/rtc-cmos.yaml
Normal file
63
Documentation/devicetree/bindings/rtc/rtc-cmos.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
...
|
||||
};
|
||||
140
Documentation/devicetree/bindings/rtc/ti,omap-rtc.yaml
Normal file
140
Documentation/devicetree/bindings/rtc/ti,omap-rtc.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
57
Documentation/devicetree/bindings/rtc/ti,palmas-rtc.yaml
Normal file
57
Documentation/devicetree/bindings/rtc/ti,palmas-rtc.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
|
|
@ -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".
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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/``
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
30
MAINTAINERS
30
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
17
Makefile
17
Makefile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
52
arch/m68k/configs/m5235evb_defconfig
Normal file
52
arch/m68k/configs/m5235evb_defconfig
Normal 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"
|
||||
51
arch/m68k/configs/m5282evb_defconfig
Normal file
51
arch/m68k/configs/m5282evb_defconfig
Normal 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"
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 ]=== */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
13
arch/um/include/asm/timex.h
Normal file
13
arch/um/include/asm/timex.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ------- */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue
Block a user