diff --git a/.github/workflows/llvm-trunk-linux-mainline.yml b/.github/workflows/llvm-trunk-linux-mainline.yml
index 6fdd942..78f3ef6 100644
--- a/.github/workflows/llvm-trunk-linux-mainline.yml
+++ b/.github/workflows/llvm-trunk-linux-mainline.yml
@@ -8,7 +8,7 @@ on:
paths:
- '.github/workflows/llvm-trunk-linux-mainline.yml'
- 'ci/linux-mainline/**'
- - 'patches/v2.0/**'
+ - 'patches/v3.0/**'
- 'scripts/**'
workflow_dispatch:
schedule:
diff --git a/README.md b/README.md
index db9605d..5f8482c 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,9 @@
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/xlab-uiuc/linux-mcdc/llvm-trunk-linux-v5.15.153.yml?label=LLVM%20trunk%2BLinux%20v5.15.153)](https://github.com/xlab-uiuc/linux-mcdc/actions/workflows/llvm-trunk-linux-v5.15.153.yml)
This repository demonstrates [KUnit](https://docs.kernel.org/dev-tools/kunit/index.html)'s
-modified condition/decision coverage (MC/DC) of 5.15.y Linux kernel using
-[Clang source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html)
-and [`llvm-cov`](https://llvm.org/docs/CommandGuide/llvm-cov.html). A patch set
-for mainline kernel is also being prepared.
+modified condition/decision coverage (MC/DC) of 5.15.y and mainline Linux kernel
+using [Clang source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html)
+and [`llvm-cov`](https://llvm.org/docs/CommandGuide/llvm-cov.html).
+**Patches can be found under [`patches/`](patches/), or join LKML discussions:
+[link](https://lore.kernel.org/lkml/20240824230641.385839-1-wentaoz5@illinois.edu/)**
+
Follow the instructions [here](docs/measure-kernel-mcdc.md) to get started.
Example text coverage report: [link](https://github.com/xlab-uiuc/linux-mcdc/actions/runs/10013137034/job/27681036852#step:8:7) (login with any GitHub account required)
@@ -24,34 +26,17 @@ Example HTML coverage report:
-Tentative repository structure:
-
-```text
-linux-mcdc
-|
-├── ci/{linux-v5.15.153}
-│ ├── 1_install_deps.sh
-│ ├── 2_pull_source.sh
-│ ├── 3_get_llvm.sh
-│ ├── 4_build_kernel.sh
-│ └── 5_boot_kernel_and_collect_coverage.sh
-|
-├── docs
-│ ├── elisa-slides.pdf
-│ └── measure-kernel-mcdc.md
-|
-├── patches
-│ ├── README.md
-│ └── {v0.4,v0.5,v0.6}
-|
-├── README.md
-|
-├── screenshot.png
-|
-└── scripts
- ├── build-llvm.sh
- └── q
-```
+We gave three talks in [LPC 2024](https://lpc.events/event/18/page/224-lpc-2024-overview):
+
+- [Making Linux Fly: Towards a Certified Linux Kernel](https://lpc.events/event/18/contributions/1718/) (Refereed Track)
+ [[recording](https://www.youtube.com/live/1KWkfHxTqYY?feature=shared&t=3957)]
+ [[slides](https://lpc.events/event/18/contributions/1718/attachments/1584/3477/LPC'24%20Fly%20(no%20animation).pdf)]
+- [Measuring and Understanding Linux Kernel Tests](https://lpc.events/event/18/contributions/1793/) (Kernel Testing & Dependability MC)
+ [[recording](https://www.youtube.com/live/kcr8NXEbzcg?feature=shared&t=9380)]
+ [[slides](https://lpc.events/event/18/contributions/1793/attachments/1624/3447/LPC'24%20Linux%20Testing.pdf)]
+- [Source-based code coverage of Linux kernel](https://lpc.events/event/18/contributions/1895/) (Safe Systems with Linux MC)
+ [[recording](https://www.youtube.com/live/kcr8NXEbzcg?feature=shared&t=23820)]
+ [[slides](https://lpc.events/event/18/contributions/1895/attachments/1643/3462/LPC'24%20Source%20based%20(short).pdf)]
We gave an [ELISA](https://elisa.tech/) seminar titled "Making Linux Fly: Towards Certified Linux
Kernel".
diff --git a/patches/latest b/patches/latest
index 9c2f1ee..26425a3 120000
--- a/patches/latest
+++ b/patches/latest
@@ -1 +1 @@
-v2.0/
\ No newline at end of file
+v3.0/
\ No newline at end of file
diff --git a/patches/v2.0/0000-cover-letter.patch b/patches/v2.0/0000-cover-letter.patch
index 45d9d82..1ae138d 100644
--- a/patches/v2.0/0000-cover-letter.patch
+++ b/patches/v2.0/0000-cover-letter.patch
@@ -1,7 +1,7 @@
From 7c60851daae39815fa696fc2265e838b1913f7ac Mon Sep 17 00:00:00 2001
From: Wentao Zhang
Date: Wed, 4 Sep 2024 18:13:07 -0500
-Subject: [RFC PATCH 0/4] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang
+Subject: [PATCH v2 0/4] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang
This series adds support for building x86-64 kernels with Clang's Source-
based Code Coverage[1] in order to facilitate Modified Condition/Decision
diff --git a/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch b/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch
index 9c8fa51..0436a11 100644
--- a/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch
+++ b/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch
@@ -1,7 +1,7 @@
From 13c86fc7877aec4892832554845968d7415f2960 Mon Sep 17 00:00:00 2001
From: Wentao Zhang
Date: Fri, 30 Aug 2024 14:48:03 -0500
-Subject: [RFC PATCH 1/4] llvm-cov: add Clang's Source-based Code Coverage
+Subject: [PATCH v2 1/4] llvm-cov: add Clang's Source-based Code Coverage
support
Add infrastructure to support Clang's source-based code coverage [1]. This
diff --git a/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch b/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch
index cf31b6b..e224983 100644
--- a/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch
+++ b/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch
@@ -1,7 +1,7 @@
From e7d38a2115b9c1041dcda404f102888c9894da10 Mon Sep 17 00:00:00 2001
From: Wentao Zhang
Date: Fri, 30 Aug 2024 14:49:24 -0500
-Subject: [RFC PATCH 2/4] llvm-cov: add Clang's MC/DC support
+Subject: [PATCH v2 2/4] llvm-cov: add Clang's MC/DC support
Add infrastructure to enable Clang's Modified Condition/Decision Coverage
(MC/DC) [1].
diff --git a/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch b/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch
index 771df8d..54851d9 100644
--- a/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch
+++ b/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch
@@ -1,7 +1,7 @@
From f4cc26b9d49b7c34671c04a8b62485a311530298 Mon Sep 17 00:00:00 2001
From: Wentao Zhang
Date: Fri, 30 Aug 2024 14:48:53 -0500
-Subject: [RFC PATCH 3/4] x86: disable llvm-cov instrumentation
+Subject: [PATCH v2 3/4] x86: disable llvm-cov instrumentation
Disable instrumentation for arch/x86/crypto/curve25519-x86_64.c. Otherwise
compilation would fail with "error: inline assembly requires more registers
diff --git a/patches/v2.0/0004-x86-enable-llvm-cov-support.patch b/patches/v2.0/0004-x86-enable-llvm-cov-support.patch
index 166d584..1e756b8 100644
--- a/patches/v2.0/0004-x86-enable-llvm-cov-support.patch
+++ b/patches/v2.0/0004-x86-enable-llvm-cov-support.patch
@@ -1,7 +1,7 @@
From 7c60851daae39815fa696fc2265e838b1913f7ac Mon Sep 17 00:00:00 2001
From: Wentao Zhang
Date: Fri, 30 Aug 2024 14:49:06 -0500
-Subject: [RFC PATCH 4/4] x86: enable llvm-cov support
+Subject: [PATCH v2 4/4] x86: enable llvm-cov support
Set ARCH_HAS_* options to "y" in kconfig and include section description in
linker script.
diff --git a/patches/v3.0/0000-cover-letter.patch b/patches/v3.0/0000-cover-letter.patch
new file mode 100644
index 0000000..0dc400c
--- /dev/null
+++ b/patches/v3.0/0000-cover-letter.patch
@@ -0,0 +1,194 @@
+From fba32f3502243c3f1bcb967667f6618897b8bf80 Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Tue, 8 Oct 2024 16:51:11 -0500
+Subject: [PATCH v3 0/5] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang
+
+This series adds support for building x86-64 kernels with Clang's Source-
+based Code Coverage[1] in order to facilitate Modified Condition/Decision
+Coverage (MC/DC)[2] that provably correlates to source code for all levels
+of compiler optimization.
+
+The newly added kernel/llvm-cov/ directory complements the existing gcov
+implementation. Gcov works at the object code level which may better
+reflect actual execution. However, Gcov lacks the necessary information to
+correlate coverage measurement with source code location when compiler
+optimization level is non-zero (which is the default when building the
+kernel). In addition, gcov reports are occasionally ambiguous when
+attempting to compare with source code level developer intent.
+
+In the following gcov example from drivers/firmware/dmi_scan.c, an
+expression with four conditions is reported to have six branch outcomes,
+which is not ideally informative in many safety related use cases, such as
+automotive, medical, and aerospace.
+
+ 5: 1068: if (s == e || *e != '/' || !month || month > 12) {
+branch 0 taken 5 (fallthrough)
+branch 1 taken 0
+branch 2 taken 5 (fallthrough)
+branch 3 taken 0
+branch 4 taken 0 (fallthrough)
+branch 5 taken 5
+
+On the other hand, Clang's Source-based Code Coverage instruments at the
+compiler frontend which maintains an accurate mapping from coverage
+measurement to source code location. Coverage reports reflect exactly how
+the code is written regardless of optimization and can present advanced
+metrics like branch coverage and MC/DC in a clearer way. Coverage report
+for the same snippet by llvm-cov would look as follows:
+
+ 1068| 5| if (s == e || *e != '/' || !month || month > 12) {
+ ------------------
+ | Branch (1068:6): [True: 0, False: 5]
+ | Branch (1068:16): [True: 0, False: 5]
+ | Branch (1068:29): [True: 0, False: 5]
+ | Branch (1068:39): [True: 0, False: 5]
+ ------------------
+
+Clang has added MC/DC support as of its 18.1.0 release. MC/DC is a fine-
+grained coverage metric required by many automotive and aviation industrial
+standards for certifying mission-critical software [3].
+
+In the following example from arch/x86/events/probe.c, llvm-cov gives the
+MC/DC measurement for the compound logic decision at line 43.
+
+ 43| 12| if (msr[bit].test && !msr[bit].test(bit, data))
+ ------------------
+ |---> MC/DC Decision Region (43:8) to (43:50)
+ |
+ | Number of Conditions: 2
+ | Condition C1 --> (43:8)
+ | Condition C2 --> (43:25)
+ |
+ | Executed MC/DC Test Vectors:
+ |
+ | C1, C2 Result
+ | 1 { T, F = F }
+ | 2 { T, T = T }
+ |
+ | C1-Pair: not covered
+ | C2-Pair: covered: (1,2)
+ | MC/DC Coverage for Decision: 50.00%
+ |
+ ------------------
+ 44| 5| continue;
+
+As the results suggest, during the span of measurement, only condition C2
+(!msr[bit].test(bit, data)) is covered. That means C2 was evaluated to both
+true and false, and in those test vectors C2 affected the decision outcome
+independently. Therefore MC/DC for this decision is 1 out of 2 (50.00%).
+
+To do a full kernel measurement, instrument the kernel with
+LLVM_COV_KERNEL_MCDC enabled, and optionally set a
+LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS value (the default is six). Run the
+testsuites, and collect the raw profile data under
+/sys/kernel/debug/llvm-cov/profraw. Such raw profile data can be merged and
+indexed, and used for generating coverage reports in various formats.
+
+ $ cp /sys/kernel/debug/llvm-cov/profraw vmlinux.profraw
+ $ llvm-profdata merge vmlinux.profraw -o vmlinux.profdata
+ $ llvm-cov show --show-mcdc --show-mcdc-summary \
+ --format=text --use-color=false -output-dir=coverage_reports \
+ -instr-profile vmlinux.profdata vmlinux
+
+The first two patches enable the llvm-cov infrastructure, where the first
+enables source based code coverage and the second adds MC/DC support. The
+next patch disables instrumentation for curve25519-x86_64.c for the same
+reason as gcov. The final patch enables coverage for x86-64.
+
+The choice to use a new Makefile variable LLVM_COV_PROFILE, instead of
+reusing GCOV_PROFILE, was deliberate. More work needs to be done to
+determine if it is appropriate to reuse the same flag. In addition, given
+the fundamentally different approaches to instrumentation and the resulting
+variation in coverage reports, there is a strong likelihood that coverage
+type will need to be managed separately.
+
+This work reuses code from a previous effort by Sami Tolvanen et al. [4].
+Our aim is for source-based *code coverage* required for high assurance
+(MC/DC) while [4] focused more on performance optimization.
+
+This initial submission is restricted to x86-64. Support for other
+architectures would need a bit more Makefile & linker script modification.
+Informally we've confirmed that arm64 works and more are being tested.
+
+Note that Source-based Code Coverage is Clang-specific and isn't compatible
+with Clang's gcov support in kernel/gcov/. Currently, kernel/gcov/ is not
+able to measure MC/DC without modifying CFLAGS_GCOV and it would face the
+same issues in terms of source correlation as gcov in general does.
+
+Some demo and results can be found in [5]. We will talk about this patch
+series in the Refereed Track at LPC 2024 [6].
+
+Known Limitations:
+
+Kernel code with logical expressions exceeding
+LVM_COV_KERNEL_MCDC_MAX_CONDITIONS will produce a compiler warning.
+Expressions with up to 47 conditions are found in the Linux kernel source
+tree (as of v6.11), but 46 seems to be the max value before the build fails
+due to kernel size. As of LLVM 19 the max number of conditions possible is
+32767.
+
+As of LLVM 19, certain expressions are still not covered, and will produce
+build warnings when they are encountered:
+
+"[...] if a boolean expression is embedded in the nest of another boolean
+ expression but separated by a non-logical operator, this is also not
+ supported. For example, in x = (a && b && c && func(d && f)), the d && f
+ case starts a new boolean expression that is separated from the other
+ conditions by the operator func(). When this is encountered, a warning
+ will be generated and the boolean expression will not be
+ instrumented." [7]
+
+
+[1] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+[2] https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage
+[3] https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025
+[4] https://lore.kernel.org/lkml/20210407211704.367039-1-morbo@google.com/
+[5] https://github.com/xlab-uiuc/linux-mcdc
+[6] https://lpc.events/event/18/contributions/1718/
+[7] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation
+
+---
+v2 -> v3:
+
+* Rebased onto v6.12-rc2 from v6.11-rc6.
+* llvm-cov:
+ * Improve documentation regarding LLVM raw profile version.
+ * Remove license boilerplate text.
+* Kconfig:
+ * Make LLVM_COV_KERNEL and GCOV_KERNEL exclusive.
+ * Improve documentation for ARCH_HAS_LLVM_COV,
+ ARCH_HAS_LLVM_COV_PROFILE_ALL and LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS.
+ * Remove "default n".
+
+---
+
+Wentao Zhang (5):
+ llvm-cov: add Clang's Source-based Code Coverage support
+ llvm-cov: add Clang's MC/DC support
+ x86: disable llvm-cov instrumentation
+ x86: enable llvm-cov support
+ MAINTAINERS: add "LLVM-COV BASED KERNEL PROFILING" entry
+
+ MAINTAINERS | 6 +
+ Makefile | 9 ++
+ arch/Kconfig | 1 +
+ arch/x86/Kconfig | 2 +
+ arch/x86/crypto/Makefile | 3 +-
+ arch/x86/kernel/vmlinux.lds.S | 2 +
+ include/asm-generic/vmlinux.lds.h | 36 +++++
+ kernel/Makefile | 1 +
+ kernel/llvm-cov/Kconfig | 118 +++++++++++++++
+ kernel/llvm-cov/Makefile | 4 +
+ kernel/llvm-cov/fs.c | 243 ++++++++++++++++++++++++++++++
+ kernel/llvm-cov/llvm-cov.h | 159 +++++++++++++++++++
+ scripts/Makefile.lib | 23 +++
+ scripts/mod/modpost.c | 2 +
+ 14 files changed, 608 insertions(+), 1 deletion(-)
+ create mode 100644 kernel/llvm-cov/Kconfig
+ create mode 100644 kernel/llvm-cov/Makefile
+ create mode 100644 kernel/llvm-cov/fs.c
+ create mode 100644 kernel/llvm-cov/llvm-cov.h
+
+--
+2.45.2
+
diff --git a/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch b/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch
new file mode 100644
index 0000000..e32919a
--- /dev/null
+++ b/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch
@@ -0,0 +1,704 @@
+From c2d7749054577e985347caabfbb21bf36af93551 Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Fri, 30 Aug 2024 14:48:03 -0500
+Subject: [PATCH v3 1/5] llvm-cov: add Clang's Source-based Code Coverage
+ support
+
+Add infrastructure to support Clang's source-based code coverage [1]. This
+includes debugfs entries for serializing profiles and resetting
+counters/bitmaps. Also adds coverage flags and kconfig options.
+
+The newly added kernel/llvm-cov/ directory complements the existing gcov
+implementation. Gcov works at the object code level which may better
+reflect actual execution. However, Gcov lacks the necessary information to
+correlate coverage measurement with source code location when compiler
+optimization level is non-zero (which is the default when building the
+kernel). In addition, gcov reports are occasionally ambiguous when
+attempting to compare with source code level developer intent.
+
+In the following gcov example from drivers/firmware/dmi_scan.c, an
+expression with four conditions is reported to have six branch outcomes,
+which is not ideally informative in many safety related use cases, such as
+automotive, medical, and aerospace.
+
+ 5: 1068: if (s == e || *e != '/' || !month || month > 12) {
+branch 0 taken 5 (fallthrough)
+branch 1 taken 0
+branch 2 taken 5 (fallthrough)
+branch 3 taken 0
+branch 4 taken 0 (fallthrough)
+branch 5 taken 5
+
+On the other hand, Clang's Source-based Code Coverage instruments at the
+compiler frontend which maintains an accurate mapping from coverage
+measurement to source code location. Coverage reports reflect exactly how
+the code is written regardless of optimization and can present advanced
+metrics like branch coverage and MC/DC in a clearer way. Coverage report
+for the same snippet by llvm-cov would look as follows:
+
+ 1068| 5| if (s == e || *e != '/' || !month || month > 12) {
+ ------------------
+ | Branch (1068:6): [True: 0, False: 5]
+ | Branch (1068:16): [True: 0, False: 5]
+ | Branch (1068:29): [True: 0, False: 5]
+ | Branch (1068:39): [True: 0, False: 5]
+ ------------------
+
+This work reuses a portion of code from a previous effort by Sami Tolvanen
+et al. [2], specifically its debugfs interface and the underlying profile
+processing, but discards all its PGO-specific parts, notably value
+profiling. To our end (code coverage required for high assurance), we
+require instrumentation at the compiler front end, instead of IR; we care
+about counters and bitmaps, but not value profiling.
+
+Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html [1]
+Link: https://lore.kernel.org/lkml/20210407211704.367039-1-morbo@google.com/ [2]
+Signed-off-by: Wentao Zhang
+Reviewed-by: Chuck Wolber
+Tested-by: Chuck Wolber
+Reviewed-by: Nathan Chancellor
+---
+ Makefile | 3 +
+ arch/Kconfig | 1 +
+ include/asm-generic/vmlinux.lds.h | 36 +++++
+ kernel/Makefile | 1 +
+ kernel/llvm-cov/Kconfig | 75 +++++++++
+ kernel/llvm-cov/Makefile | 4 +
+ kernel/llvm-cov/fs.c | 243 ++++++++++++++++++++++++++++++
+ kernel/llvm-cov/llvm-cov.h | 159 +++++++++++++++++++
+ scripts/Makefile.lib | 11 ++
+ scripts/mod/modpost.c | 2 +
+ 10 files changed, 535 insertions(+)
+ create mode 100644 kernel/llvm-cov/Kconfig
+ create mode 100644 kernel/llvm-cov/Makefile
+ create mode 100644 kernel/llvm-cov/fs.c
+ create mode 100644 kernel/llvm-cov/llvm-cov.h
+
+diff --git a/Makefile b/Makefile
+index c5493c0c0..1823f04fb 100644
+--- a/Makefile
++++ b/Makefile
+@@ -736,6 +736,9 @@ endif # KBUILD_EXTMOD
+ # Defaults to vmlinux, but the arch makefile usually adds further targets
+ all: vmlinux
+
++CFLAGS_LLVM_COV := -fprofile-instr-generate -fcoverage-mapping
++export CFLAGS_LLVM_COV
++
+ CFLAGS_GCOV := -fprofile-arcs -ftest-coverage
+ ifdef CONFIG_CC_IS_GCC
+ CFLAGS_GCOV += -fno-tree-loop-im
+diff --git a/arch/Kconfig b/arch/Kconfig
+index 8af374ea1..6abf9dc2a 100644
+--- a/arch/Kconfig
++++ b/arch/Kconfig
+@@ -1642,6 +1642,7 @@ config ARCH_HAS_KERNEL_FPU_SUPPORT
+ the kernel, as described in Documentation/core-api/floating-point.rst.
+
+ source "kernel/gcov/Kconfig"
++source "kernel/llvm-cov/Kconfig"
+
+ source "scripts/gcc-plugins/Kconfig"
+
+diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
+index eeadbaecc..c8a333157 100644
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -335,6 +335,42 @@
+ #define THERMAL_TABLE(name)
+ #endif
+
++#ifdef CONFIG_LLVM_COV_KERNEL
++#define LLVM_COV_DATA \
++ __llvm_prf_data : AT(ADDR(__llvm_prf_data) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_data, \
++ __llvm_prf_data, \
++ _start, _end) \
++ } \
++ __llvm_prf_cnts : AT(ADDR(__llvm_prf_cnts) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_cnts, \
++ __llvm_prf_cnts, \
++ _start, _end) \
++ } \
++ __llvm_prf_names : AT(ADDR(__llvm_prf_names) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_names, \
++ __llvm_prf_names, \
++ _start, _end) \
++ } \
++ __llvm_prf_bits : AT(ADDR(__llvm_prf_bits) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_bits, \
++ __llvm_prf_bits, \
++ _start, _end) \
++ } \
++ __llvm_covfun : AT(ADDR(__llvm_covfun) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_covfun, \
++ __llvm_covfun, \
++ _start, _end) \
++ } \
++ __llvm_covmap : AT(ADDR(__llvm_covmap) - LOAD_OFFSET) { \
++ BOUNDED_SECTION_POST_LABEL(__llvm_covmap, \
++ __llvm_covmap, \
++ _start, _end) \
++ }
++#else
++#define LLVM_COV_DATA
++#endif
++
+ #define KERNEL_DTB() \
+ STRUCT_ALIGN(); \
+ __dtb_start = .; \
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 87866b037..ea63bec02 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -116,6 +116,7 @@ obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
+ obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o
+ obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) += static_call_inline.o
+ obj-$(CONFIG_CFI_CLANG) += cfi.o
++obj-$(CONFIG_LLVM_COV_KERNEL) += llvm-cov/
+
+ obj-$(CONFIG_PERF_EVENTS) += events/
+
+diff --git a/kernel/llvm-cov/Kconfig b/kernel/llvm-cov/Kconfig
+new file mode 100644
+index 000000000..e0533be92
+--- /dev/null
++++ b/kernel/llvm-cov/Kconfig
+@@ -0,0 +1,75 @@
++# SPDX-License-Identifier: GPL-2.0-only
++menu "Clang's source-based kernel coverage measurement (EXPERIMENTAL)"
++
++config ARCH_HAS_LLVM_COV
++ bool
++ help
++ An architecture can select this when it can successfully build and run
++ with CONFIG_LLVM_COV_KERNEL. This typically requires (1) disabling
++ instrumentation for object files that otherwise result in build
++ failures and (2) including LLVM_COV_DATA in the linker script so that
++ relevant sections are placed correctly into vmlinux.
++
++config ARCH_HAS_LLVM_COV_PROFILE_ALL
++ bool
++ help
++ An architecture can select this when it can successfully build and run
++ with CONFIG_LLVM_COV_PROFILE_ALL. This typically requires
++ (1) disabling instrumentation for object files that otherwise result
++ in build failures and (2) including LLVM_COV_DATA in the linker script
++ so that relevant sections are placed correctly into vmlinux.
++
++config LLVM_COV_KERNEL
++ bool "Enable Clang's source-based kernel coverage measurement"
++ depends on DEBUG_FS
++ depends on ARCH_HAS_LLVM_COV
++ depends on CC_IS_CLANG && CLANG_VERSION >= 180000
++ depends on !GCOV_KERNEL
++ help
++ This option enables Clang's Source-based Code Coverage.
++
++ If unsure, say N.
++
++ On a kernel compiled with this option, run your test suites, and
++ download the raw profile from /sys/kernel/debug/llvm-cov/profraw.
++ This file can then be converted into the indexed format with
++ llvm-profdata and used to generate coverage reports with llvm-cov.
++
++ Additionally specify CONFIG_LLVM_COV_PROFILE_ALL=y to get profiling
++ data for the entire kernel. To enable profiling for specific files or
++ directories, add a line similar to the following to the respective
++ Makefile:
++
++ For a single file (e.g. main.o):
++ LLVM_COV_PROFILE_main.o := y
++
++ For all files in one directory:
++ LLVM_COV_PROFILE := y
++
++ To exclude files from being profiled even when
++ CONFIG_LLVM_COV_PROFILE_ALL is specified, use:
++
++ LLVM_COV_PROFILE_main.o := n
++ and:
++ LLVM_COV_PROFILE := n
++
++ Note that a kernel compiled with coverage flags will be significantly
++ larger and run slower.
++
++ Note that the debugfs filesystem has to be mounted to access the raw
++ profile.
++
++config LLVM_COV_PROFILE_ALL
++ bool "Profile entire Kernel"
++ depends on !COMPILE_TEST
++ depends on LLVM_COV_KERNEL
++ depends on ARCH_HAS_LLVM_COV_PROFILE_ALL
++ help
++ This options activates profiling for the entire kernel.
++
++ If unsure, say N.
++
++ Note that a kernel compiled with profiling flags will be significantly
++ larger and run slower.
++
++endmenu
+diff --git a/kernel/llvm-cov/Makefile b/kernel/llvm-cov/Makefile
+new file mode 100644
+index 000000000..1a03a88c7
+--- /dev/null
++++ b/kernel/llvm-cov/Makefile
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
++LLVM_COV_PROFILE := n
++
++obj-y += fs.o
+diff --git a/kernel/llvm-cov/fs.c b/kernel/llvm-cov/fs.c
+new file mode 100644
+index 000000000..363064bf9
+--- /dev/null
++++ b/kernel/llvm-cov/fs.c
+@@ -0,0 +1,243 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2019 Sami Tolvanen , Google, Inc.
++ * Copyright (C) 2024 Jinghao Jia , UIUC
++ * Copyright (C) 2024 Wentao Zhang , UIUC
++ */
++
++#define pr_fmt(fmt) "llvm-cov: " fmt
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "llvm-cov.h"
++
++/*
++ * This lock guards both counter/bitmap reset and serialization of the
++ * raw profile data. Keeping both of these activities separate via locking
++ * ensures that we don't try to serialize data that's being reset.
++ */
++DEFINE_SPINLOCK(llvm_cov_lock);
++
++static struct dentry *directory;
++
++struct llvm_cov_private_data {
++ char *buffer;
++ unsigned long size;
++};
++
++/*
++ * Raw profile data format:
++ * https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format. We will
++ * only populate information that's relevant to basic Source-based Code Coverage
++ * before serialization. Other features like binary IDs, continuous mode,
++ * single-byte mode, value profiling, type profiling etc are not implemented.
++ */
++
++static void llvm_cov_fill_raw_profile_header(void **buffer)
++{
++ struct __llvm_profile_header *header = *(struct __llvm_profile_header **)buffer;
++
++ header->magic = INSTR_PROF_RAW_MAGIC_64;
++ header->version = INSTR_PROF_RAW_VERSION;
++ header->binary_ids_size = 0;
++ header->num_data = __llvm_prf_data_count();
++ header->padding_bytes_before_counters = 0;
++ header->num_counters = __llvm_prf_cnts_count();
++ header->padding_bytes_after_counters =
++ __llvm_prf_get_padding(__llvm_prf_cnts_size());
++ header->num_bitmap_bytes = __llvm_prf_bits_size();
++ header->padding_bytes_after_bitmap_bytes =
++ __llvm_prf_get_padding(__llvm_prf_bits_size());
++ header->names_size = __llvm_prf_names_size();
++ header->counters_delta = (u64)__llvm_prf_cnts_start -
++ (u64)__llvm_prf_data_start;
++ header->bitmap_delta = (u64)__llvm_prf_bits_start -
++ (u64)__llvm_prf_data_start;
++ header->names_delta = (u64)__llvm_prf_names_start;
++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000
++ header->num_v_tables = 0;
++ header->v_names_size = 0;
++#endif
++ header->value_kind_last = IPVK_LAST;
++
++ *buffer += sizeof(*header);
++}
++
++/*
++ * Copy the source into the buffer, incrementing the pointer into buffer in the
++ * process.
++ */
++static void llvm_cov_copy_section_to_buffer(void **buffer, void *src,
++ unsigned long size)
++{
++ memcpy(*buffer, src, size);
++ *buffer += size;
++}
++
++static unsigned long llvm_cov_get_raw_profile_size(void)
++{
++ return sizeof(struct __llvm_profile_header) +
++ __llvm_prf_data_size() +
++ __llvm_prf_cnts_size() +
++ __llvm_prf_get_padding(__llvm_prf_cnts_size()) +
++ __llvm_prf_bits_size() +
++ __llvm_prf_get_padding(__llvm_prf_bits_size()) +
++ __llvm_prf_names_size() +
++ __llvm_prf_get_padding(__llvm_prf_names_size());
++}
++
++/*
++ * Serialize in-memory data into a format LLVM tools can understand
++ * (https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format)
++ */
++static int llvm_cov_serialize_raw_profile(struct llvm_cov_private_data *p)
++{
++ int err = 0;
++ void *buffer;
++
++ p->size = llvm_cov_get_raw_profile_size();
++ p->buffer = vzalloc(p->size);
++
++ if (!p->buffer) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ buffer = p->buffer;
++
++ llvm_cov_fill_raw_profile_header(&buffer);
++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_data_start,
++ __llvm_prf_data_size());
++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_cnts_start,
++ __llvm_prf_cnts_size());
++ buffer += __llvm_prf_get_padding(__llvm_prf_cnts_size());
++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_bits_start,
++ __llvm_prf_bits_size());
++ buffer += __llvm_prf_get_padding(__llvm_prf_bits_size());
++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_names_start,
++ __llvm_prf_names_size());
++ buffer += __llvm_prf_get_padding(__llvm_prf_names_size());
++
++out:
++ return err;
++}
++
++/* open() implementation for llvm-cov data file. */
++static int llvm_cov_open(struct inode *inode, struct file *file)
++{
++ struct llvm_cov_private_data *data;
++ unsigned long flags;
++ int err;
++
++ data = kzalloc(sizeof(*data), GFP_KERNEL);
++ if (!data) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ flags = llvm_cov_claim_lock();
++
++ err = llvm_cov_serialize_raw_profile(data);
++ if (unlikely(err)) {
++ kfree(data);
++ goto out_unlock;
++ }
++
++ file->private_data = data;
++
++out_unlock:
++ llvm_cov_release_lock(flags);
++out:
++ return err;
++}
++
++/* read() implementation for llvm-cov data file. */
++static ssize_t llvm_cov_read(struct file *file, char __user *buf, size_t count,
++ loff_t *ppos)
++{
++ struct llvm_cov_private_data *data = file->private_data;
++
++ if (!data)
++ return -EBADF;
++
++ return simple_read_from_buffer(buf, count, ppos, data->buffer,
++ data->size);
++}
++
++/* release() implementation for llvm-cov data file. */
++static int llvm_cov_release(struct inode *inode, struct file *file)
++{
++ struct llvm_cov_private_data *data = file->private_data;
++
++ if (data) {
++ vfree(data->buffer);
++ kfree(data);
++ }
++
++ return 0;
++}
++
++static const struct file_operations llvm_cov_data_fops = {
++ .owner = THIS_MODULE,
++ .open = llvm_cov_open,
++ .read = llvm_cov_read,
++ .llseek = default_llseek,
++ .release = llvm_cov_release
++};
++
++/* write() implementation for llvm-cov reset file */
++static ssize_t reset_write(struct file *file, const char __user *addr,
++ size_t len, loff_t *pos)
++{
++ unsigned long flags;
++
++ flags = llvm_cov_claim_lock();
++ memset(__llvm_prf_cnts_start, 0, __llvm_prf_cnts_size());
++ memset(__llvm_prf_bits_start, 0, __llvm_prf_bits_size());
++ llvm_cov_release_lock(flags);
++
++ return len;
++}
++
++static const struct file_operations llvm_cov_reset_fops = {
++ .owner = THIS_MODULE,
++ .write = reset_write,
++ .llseek = noop_llseek,
++};
++
++/* Create debugfs entries. */
++static int __init llvm_cov_init(void)
++{
++ directory = debugfs_create_dir("llvm-cov", NULL);
++ if (!directory)
++ goto err_remove;
++
++ if (!debugfs_create_file("profraw", 0400, directory, NULL,
++ &llvm_cov_data_fops))
++ goto err_remove;
++
++ if (!debugfs_create_file("reset", 0200, directory, NULL,
++ &llvm_cov_reset_fops))
++ goto err_remove;
++
++ return 0;
++
++err_remove:
++ debugfs_remove_recursive(directory);
++ pr_err("initialization failed\n");
++ return -EIO;
++}
++
++/* Remove debugfs entries. */
++static void __exit llvm_cov_exit(void)
++{
++ debugfs_remove_recursive(directory);
++}
++
++module_init(llvm_cov_init);
++module_exit(llvm_cov_exit);
+diff --git a/kernel/llvm-cov/llvm-cov.h b/kernel/llvm-cov/llvm-cov.h
+new file mode 100644
+index 000000000..d807f1b57
+--- /dev/null
++++ b/kernel/llvm-cov/llvm-cov.h
+@@ -0,0 +1,159 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2019 Sami Tolvanen , Google, Inc.
++ * Copyright (C) 2024 Jinghao Jia , UIUC
++ * Copyright (C) 2024 Wentao Zhang , UIUC
++ */
++
++#ifndef _LLVM_COV_H
++#define _LLVM_COV_H
++
++extern spinlock_t llvm_cov_lock;
++
++static __always_inline unsigned long llvm_cov_claim_lock(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&llvm_cov_lock, flags);
++
++ return flags;
++}
++
++static __always_inline void llvm_cov_release_lock(unsigned long flags)
++{
++ spin_unlock_irqrestore(&llvm_cov_lock, flags);
++}
++
++/*
++ * Note: These internal LLVM definitions must match the compiler version.
++ * See llvm/include/llvm/ProfileData/InstrProfData.inc in LLVM's source code.
++ */
++
++#define INSTR_PROF_RAW_MAGIC_64 \
++ ((u64)255 << 56 | \
++ (u64)'l' << 48 | \
++ (u64)'p' << 40 | \
++ (u64)'r' << 32 | \
++ (u64)'o' << 24 | \
++ (u64)'f' << 16 | \
++ (u64)'r' << 8 | \
++ (u64)129)
++
++/*
++ * Version bump in LLVM commit db7e9e68411d, in which code coverage
++ * functionality (llvm-cov) is unaffected.
++ */
++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000
++#define INSTR_PROF_RAW_VERSION 10
++#define INSTR_PROF_DATA_ALIGNMENT 8
++#define IPVK_LAST 2
++/*
++ * Version bump in LLVM commit a50486fd736a, which adds MC/DC support.
++ * Version 9 is the minimal version of raw profile supported in kernel.
++ */
++#elif defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 180000
++#define INSTR_PROF_RAW_VERSION 9
++#define INSTR_PROF_DATA_ALIGNMENT 8
++#define IPVK_LAST 1
++#endif
++
++/*
++ * struct __llvm_profile_header - represents the raw profile header data
++ * structure. Description of each member can be found here:
++ * https://llvm.org/docs/InstrProfileFormat.html#header.
++ */
++struct __llvm_profile_header {
++ u64 magic;
++ u64 version;
++ u64 binary_ids_size;
++ u64 num_data;
++ u64 padding_bytes_before_counters;
++ u64 num_counters;
++ u64 padding_bytes_after_counters;
++ u64 num_bitmap_bytes;
++ u64 padding_bytes_after_bitmap_bytes;
++ u64 names_size;
++ u64 counters_delta;
++ u64 bitmap_delta;
++ u64 names_delta;
++/*
++ * Version bump in LLVM commit db7e9e68411d, in which code coverage
++ * functionality (llvm-cov) is unaffected. These newly introduced
++ * fields will be populated with their default values.
++ */
++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000
++ u64 num_v_tables;
++ u64 v_names_size;
++#endif
++ u64 value_kind_last;
++};
++
++/*
++ * struct __llvm_profile_data - represents the per-function control structure.
++ * Description of each member can be found here:
++ * https://llvm.org/docs/InstrProfileFormat.html#profile-metadata. To measure
++ * Source-based Code Coverage, the internals of this struct don't matter at run
++ * time. The only purpose of the definition below is to run sizeof() against it
++ * so that we can calculate the "num_data" field in header.
++ */
++struct __llvm_profile_data {
++ const u64 name_ref;
++ const u64 func_hash;
++ const void *counter_ptr;
++ const void *bitmap_ptr;
++ const void *function_pointer;
++ void *values;
++ const u32 num_counters;
++ const u16 num_value_sites[IPVK_LAST + 1];
++ const u32 num_bitmap_bytes;
++} __aligned(INSTR_PROF_DATA_ALIGNMENT);
++
++/* Payload sections */
++
++extern struct __llvm_profile_data __llvm_prf_data_start[];
++extern struct __llvm_profile_data __llvm_prf_data_end[];
++
++extern u64 __llvm_prf_cnts_start[];
++extern u64 __llvm_prf_cnts_end[];
++
++extern char __llvm_prf_names_start[];
++extern char __llvm_prf_names_end[];
++
++extern char __llvm_prf_bits_start[];
++extern char __llvm_prf_bits_end[];
++
++#define __DEFINE_SECTION_SIZE(s) \
++ static inline unsigned long __llvm_prf_ ## s ## _size(void) \
++ { \
++ unsigned long start = \
++ (unsigned long)__llvm_prf_ ## s ## _start; \
++ unsigned long end = \
++ (unsigned long)__llvm_prf_ ## s ## _end; \
++ return end - start; \
++ }
++#define __DEFINE_SECTION_COUNT(s) \
++ static inline unsigned long __llvm_prf_ ## s ## _count(void) \
++ { \
++ return __llvm_prf_ ## s ## _size() / \
++ sizeof(__llvm_prf_ ## s ## _start[0]); \
++ }
++
++__DEFINE_SECTION_SIZE(data)
++__DEFINE_SECTION_SIZE(cnts)
++__DEFINE_SECTION_SIZE(names)
++__DEFINE_SECTION_SIZE(bits)
++
++__DEFINE_SECTION_COUNT(data)
++__DEFINE_SECTION_COUNT(cnts)
++__DEFINE_SECTION_COUNT(names)
++__DEFINE_SECTION_COUNT(bits)
++
++#undef __DEFINE_SECTION_SIZE
++#undef __DEFINE_SECTION_COUNT
++
++static inline unsigned long __llvm_prf_get_padding(unsigned long size)
++{
++ return 7 & (sizeof(u64) - size % sizeof(u64));
++}
++
++#endif /* _LLVM_COV_H */
+diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
+index 01a9f567d..5bf907575 100644
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -137,6 +137,17 @@ _c_flags += $(if $(patsubst n%,, \
+ $(CFLAGS_GCOV))
+ endif
+
++#
++# Enable Clang's Source-based Code Coverage flags for a file or directory
++# depending on variables LLVM_COV_PROFILE_obj.o, LLVM_COV_PROFILE and
++# CONFIG_LLVM_COV_PROFILE_ALL.
++#
++ifeq ($(CONFIG_LLVM_COV_KERNEL),y)
++_c_flags += $(if $(patsubst n%,, \
++ $(LLVM_COV_PROFILE_$(target-stem).o)$(LLVM_COV_PROFILE)$(if $(is-kernel-object),$(CONFIG_LLVM_COV_PROFILE_ALL))), \
++ $(CFLAGS_LLVM_COV))
++endif
++
+ #
+ # Enable address sanitizer flags for kernel except some files or directories
+ # we don't want to check (depends on variables KASAN_SANITIZE_obj.o, KASAN_SANITIZE)
+diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
+index 107393a8c..9a287074c 100644
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -742,6 +742,8 @@ static const char *const section_white_list[] =
+ ".gnu.lto*",
+ ".discard.*",
+ ".llvm.call-graph-profile", /* call graph */
++ "__llvm_covfun",
++ "__llvm_covmap",
+ NULL
+ };
+
+--
+2.45.2
+
diff --git a/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch b/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch
new file mode 100644
index 0000000..1b72bc8
--- /dev/null
+++ b/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch
@@ -0,0 +1,175 @@
+From 5f7e376a3d4974b54c075dcc5094df7f30c44863 Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Fri, 30 Aug 2024 14:49:24 -0500
+Subject: [PATCH v3 2/5] llvm-cov: add Clang's MC/DC support
+
+Add infrastructure to enable Clang's Modified Condition/Decision Coverage
+(MC/DC) [1].
+
+Clang has added MC/DC support as of its 18.1.0 release. MC/DC is a fine-
+grained coverage metric required by many automotive and aviation industrial
+standards for certifying mission-critical software [2].
+
+In the following example from arch/x86/events/probe.c, llvm-cov gives the
+MC/DC measurement for the compound logic decision at line 43.
+
+ 43| 12| if (msr[bit].test && !msr[bit].test(bit, data))
+ ------------------
+ |---> MC/DC Decision Region (43:8) to (43:50)
+ |
+ | Number of Conditions: 2
+ | Condition C1 --> (43:8)
+ | Condition C2 --> (43:25)
+ |
+ | Executed MC/DC Test Vectors:
+ |
+ | C1, C2 Result
+ | 1 { T, F = F }
+ | 2 { T, T = T }
+ |
+ | C1-Pair: not covered
+ | C2-Pair: covered: (1,2)
+ | MC/DC Coverage for Decision: 50.00%
+ |
+ ------------------
+ 44| 5| continue;
+
+As the results suggest, during the span of measurement, only condition C2
+(!msr[bit].test(bit, data)) is covered. That means C2 was evaluated to both
+true and false, and in those test vectors C2 affected the decision outcome
+independently. Therefore MC/DC for this decision is 1 out of 2 (50.00%).
+
+As of Clang 19, users can determine the max number of conditions in a
+decision to measure via option LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS, which
+controls -fmcdc-max-conditions flag of Clang cc1 [3]. Since MC/DC
+implementation utilizes bitmaps to track the execution of test vectors,
+more memory is consumed if larger decisions are getting counted. The
+maximum value supported by Clang is 32767. According to local experiments,
+the working maximum for Linux kernel is 46, with the largest decisions in
+kernel codebase (with 47 conditions, as of v6.11) excluded, otherwise the
+kernel image size limit will be exceeded. The largest decisions in kernel
+are contributed for example by macros checking CPUID.
+
+Code exceeding LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS will produce compiler
+warnings.
+
+As of LLVM 19, certain expressions are still not covered, and will produce
+build warnings when they are encountered:
+
+"[...] if a boolean expression is embedded in the nest of another boolean
+ expression but separated by a non-logical operator, this is also not
+ supported. For example, in x = (a && b && c && func(d && f)), the d && f
+ case starts a new boolean expression that is separated from the other
+ conditions by the operator func(). When this is encountered, a warning
+ will be generated and the boolean expression will not be
+ instrumented." [4]
+
+Link: https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage [1]
+Link: https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025 [2]
+Link: https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798 [3]
+Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation [4]
+Signed-off-by: Wentao Zhang
+Reviewed-by: Chuck Wolber
+Tested-by: Chuck Wolber
+Reviewed-by: Nathan Chancellor
+---
+ Makefile | 6 ++++++
+ kernel/llvm-cov/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++
+ scripts/Makefile.lib | 12 ++++++++++++
+ 3 files changed, 61 insertions(+)
+
+diff --git a/Makefile b/Makefile
+index 1823f04fb..e03d16c16 100644
+--- a/Makefile
++++ b/Makefile
+@@ -739,6 +739,12 @@ all: vmlinux
+ CFLAGS_LLVM_COV := -fprofile-instr-generate -fcoverage-mapping
+ export CFLAGS_LLVM_COV
+
++CFLAGS_LLVM_COV_MCDC := -fcoverage-mcdc
++ifdef CONFIG_LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS
++CFLAGS_LLVM_COV_MCDC += -Xclang -fmcdc-max-conditions=$(CONFIG_LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS)
++endif
++export CFLAGS_LLVM_COV_MCDC
++
+ CFLAGS_GCOV := -fprofile-arcs -ftest-coverage
+ ifdef CONFIG_CC_IS_GCC
+ CFLAGS_GCOV += -fno-tree-loop-im
+diff --git a/kernel/llvm-cov/Kconfig b/kernel/llvm-cov/Kconfig
+index e0533be92..4f0290753 100644
+--- a/kernel/llvm-cov/Kconfig
++++ b/kernel/llvm-cov/Kconfig
+@@ -72,4 +72,47 @@ config LLVM_COV_PROFILE_ALL
+ Note that a kernel compiled with profiling flags will be significantly
+ larger and run slower.
+
++config LLVM_COV_KERNEL_MCDC
++ bool "Enable measuring modified condition/decision coverage (MC/DC)"
++ depends on LLVM_COV_KERNEL
++ depends on CLANG_VERSION >= 180000
++ help
++ This option enables modified condition/decision coverage (MC/DC)
++ code coverage instrumentation.
++
++ If unsure, say N.
++
++ This will add Clang's Source-based Code Coverage MC/DC
++ instrumentation to your kernel. As of LLVM 19, certain expressions
++ are still not covered, and will produce build warnings when they are
++ encountered.
++
++ "[...] if a boolean expression is embedded in the nest of another
++ boolean expression but separated by a non-logical operator, this is
++ also not supported. For example, in
++ x = (a && b && c && func(d && f)), the d && f case starts a new
++ boolean expression that is separated from the other conditions by the
++ operator func(). When this is encountered, a warning will be
++ generated and the boolean expression will not be instrumented."
++
++ https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation
++
++config LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS
++ int "Maximum number of conditions in a decision to instrument"
++ range 6 32767
++ depends on LLVM_COV_KERNEL_MCDC
++ depends on CLANG_VERSION >= 190000
++ default "6"
++ help
++ This value is passed to "-fmcdc-max-conditions" flag of Clang cc1.
++ Expressions whose number of conditions is greater than this value will
++ produce warnings and will not be instrumented.
++
++ Since MC/DC implementation utilizes bitmaps to track the execution of
++ test vectors, more memory is consumed if larger decisions are getting
++ counted. The maximum value supported by Clang is 32767. In practice,
++ the working maximum for Linux kernel depends on the version and
++ configuration and is much smaller (for example, 47 as for defconfig of
++ v6.11). Otherwise the kernel image size limit will be exceeded.
++
+ endmenu
+diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
+index 5bf907575..36d1c41f4 100644
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -148,6 +148,18 @@ _c_flags += $(if $(patsubst n%,, \
+ $(CFLAGS_LLVM_COV))
+ endif
+
++#
++# Flag that turns on modified condition/decision coverage (MC/DC) measurement
++# with Clang's Source-based Code Coverage. Enable the flag for a file or
++# directory depending on variables LLVM_COV_PROFILE_obj.o, LLVM_COV_PROFILE and
++# CONFIG_LLVM_COV_PROFILE_ALL.
++#
++ifeq ($(CONFIG_LLVM_COV_KERNEL_MCDC),y)
++_c_flags += $(if $(patsubst n%,, \
++ $(LLVM_COV_PROFILE_$(target-stem).o)$(LLVM_COV_PROFILE)$(if $(is-kernel-object),$(CONFIG_LLVM_COV_PROFILE_ALL))), \
++ $(CFLAGS_LLVM_COV_MCDC))
++endif
++
+ #
+ # Enable address sanitizer flags for kernel except some files or directories
+ # we don't want to check (depends on variables KASAN_SANITIZE_obj.o, KASAN_SANITIZE)
+--
+2.45.2
+
diff --git a/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch b/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch
new file mode 100644
index 0000000..4a49863
--- /dev/null
+++ b/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch
@@ -0,0 +1,35 @@
+From 955a004f004ccb7df2bfd32a1e72f9e4d83343ed Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Fri, 30 Aug 2024 14:48:53 -0500
+Subject: [PATCH v3 3/5] x86: disable llvm-cov instrumentation
+
+Disable instrumentation for arch/x86/crypto/curve25519-x86_64.c. Otherwise
+compilation would fail with "error: inline assembly requires more registers
+than available".
+
+Similar behavior was reported with gcov as well. See c390c452ebeb ("crypto:
+x86/curve25519 - disable gcov").
+
+Signed-off-by: Wentao Zhang
+Reviewed-by: Chuck Wolber
+Tested-by: Chuck Wolber
+Reviewed-by: Nathan Chancellor
+---
+ arch/x86/crypto/Makefile | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile
+index 53b4a2778..57f3d4921 100644
+--- a/arch/x86/crypto/Makefile
++++ b/arch/x86/crypto/Makefile
+@@ -119,5 +119,6 @@ quiet_cmd_perlasm = PERLASM $@
+ $(obj)/%.S: $(src)/%.pl FORCE
+ $(call if_changed,perlasm)
+
+-# Disable GCOV in odd or sensitive code
++# Disable GCOV and llvm-cov in odd or sensitive code
+ GCOV_PROFILE_curve25519-x86_64.o := n
++LLVM_COV_PROFILE_curve25519-x86_64.o := n
+--
+2.45.2
+
diff --git a/patches/v3.0/0004-x86-enable-llvm-cov-support.patch b/patches/v3.0/0004-x86-enable-llvm-cov-support.patch
new file mode 100644
index 0000000..845cd5d
--- /dev/null
+++ b/patches/v3.0/0004-x86-enable-llvm-cov-support.patch
@@ -0,0 +1,46 @@
+From d9d79d28f719b831d4cd2bc0d5031cfe0d01aedd Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Fri, 30 Aug 2024 14:49:06 -0500
+Subject: [PATCH v3 4/5] x86: enable llvm-cov support
+
+Set ARCH_HAS_* options to "y" in kconfig and include sections related to
+llvm-cov in vmlinux.
+
+Signed-off-by: Wentao Zhang
+Reviewed-by: Chuck Wolber
+Tested-by: Chuck Wolber
+Reviewed-by: Nathan Chancellor
+---
+ arch/x86/Kconfig | 2 ++
+ arch/x86/kernel/vmlinux.lds.S | 2 ++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
+index 2852fcd82..4dff48085 100644
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -87,6 +87,8 @@ config X86
+ select ARCH_HAS_FORTIFY_SOURCE
+ select ARCH_HAS_GCOV_PROFILE_ALL
+ select ARCH_HAS_KCOV if X86_64
++ select ARCH_HAS_LLVM_COV if X86_64
++ select ARCH_HAS_LLVM_COV_PROFILE_ALL if X86_64
+ select ARCH_HAS_KERNEL_FPU_SUPPORT
+ select ARCH_HAS_MEM_ENCRYPT
+ select ARCH_HAS_MEMBARRIER_SYNC_CORE
+diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
+index 6726be89b..13504e746 100644
+--- a/arch/x86/kernel/vmlinux.lds.S
++++ b/arch/x86/kernel/vmlinux.lds.S
+@@ -191,6 +191,8 @@ SECTIONS
+
+ BUG_TABLE
+
++ LLVM_COV_DATA
++
+ ORC_UNWIND_TABLE
+
+ . = ALIGN(PAGE_SIZE);
+--
+2.45.2
+
diff --git a/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch b/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch
new file mode 100644
index 0000000..e8a7998
--- /dev/null
+++ b/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch
@@ -0,0 +1,33 @@
+From fba32f3502243c3f1bcb967667f6618897b8bf80 Mon Sep 17 00:00:00 2001
+From: Wentao Zhang
+Date: Tue, 8 Oct 2024 16:47:23 -0500
+Subject: [PATCH v3 5/5] MAINTAINERS: add "LLVM-COV BASED KERNEL PROFILING"
+ entry
+
+Add Wentao Zhang and Chuck Wolber as the maintainer for llvm-cov.
+
+Signed-off-by: Wentao Zhang
+---
+ MAINTAINERS | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index a097afd76..55438cb90 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -13262,6 +13262,12 @@ F: include/net/llc*
+ F: include/uapi/linux/llc.h
+ F: net/llc/
+
++LLVM-COV BASED KERNEL PROFILING
++M: Wentao Zhang
++M: Chuck Wolber
++S: Maintained
++F: kernel/llvm-cov/
++
+ LM73 HARDWARE MONITOR DRIVER
+ M: Guillaume Ligneul
+ L: linux-hwmon@vger.kernel.org
+--
+2.45.2
+
diff --git a/patches/v3.0/README.md b/patches/v3.0/README.md
new file mode 100644
index 0000000..eba0a2e
--- /dev/null
+++ b/patches/v3.0/README.md
@@ -0,0 +1 @@
+These patches were tested on Linux Kernel 6.12-rc2.