Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dfebd797c | ||
|
|
c8eb56a851 | ||
|
|
85dfd73d1c |
@@ -194,6 +194,13 @@ Optional Properties:
|
||||
Specify the number of macrotiling channels for this chip.
|
||||
This is programmed into certain registers and also pass to
|
||||
the user as a property.
|
||||
- qcom,l2pc-cpu-mask:
|
||||
Disables L2PC on masked CPUs when any of Graphics
|
||||
rendering thread is running on masked CPUs.
|
||||
Bit 0 is for CPU-0, bit 1 is for CPU-1...
|
||||
|
||||
- qcom,l2pc-update-queue:
|
||||
Disables L2PC on masked CPUs at queue time when it's true.
|
||||
|
||||
- qcom,snapshot-size:
|
||||
Specify the size of snapshot in bytes. This will override
|
||||
|
||||
13
MAINTAINERS
13
MAINTAINERS
@@ -9057,19 +9057,6 @@ S: Maintained
|
||||
F: arch/arm/boot/dts/mmp*
|
||||
F: arch/arm/mach-mmp/
|
||||
|
||||
MMU GATHER AND TLB INVALIDATION
|
||||
M: Will Deacon <will.deacon@arm.com>
|
||||
M: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: Nick Piggin <npiggin@gmail.com>
|
||||
M: Peter Zijlstra <peterz@infradead.org>
|
||||
L: linux-arch@vger.kernel.org
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
F: arch/*/include/asm/tlb.h
|
||||
F: include/asm-generic/tlb.h
|
||||
F: mm/mmu_gather.c
|
||||
|
||||
MN88472 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm-generic/pci_iomap.h>
|
||||
#include <linux/msm_rtb.h>
|
||||
#include <xen/xen.h>
|
||||
|
||||
/*
|
||||
@@ -61,23 +62,24 @@ void __raw_readsl(const volatile void __iomem *addr, void *data, int longlen);
|
||||
* the bus. Rather than special-case the machine, just let the compiler
|
||||
* generate the access for CPUs prior to ARMv6.
|
||||
*/
|
||||
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
|
||||
#define __raw_writew(v,a) ((void)(__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)))
|
||||
#define __raw_readw_no_log(a) (__chk_io_ptr(a), \
|
||||
*(volatile unsigned short __force *)(a))
|
||||
#define __raw_writew_no_log(v, a) ((void)(__chk_io_ptr(a), \
|
||||
*(volatile unsigned short __force *)\
|
||||
(a) = (v)))
|
||||
#else
|
||||
/*
|
||||
* When running under a hypervisor, we want to avoid I/O accesses with
|
||||
* writeback addressing modes as these incur a significant performance
|
||||
* overhead (the address generation must be emulated in software).
|
||||
*/
|
||||
#define __raw_writew __raw_writew
|
||||
static inline void __raw_writew(u16 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writew_no_log(u16 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("strh %1, %0"
|
||||
: : "Q" (*(volatile u16 __force *)addr), "r" (val));
|
||||
}
|
||||
|
||||
#define __raw_readw __raw_readw
|
||||
static inline u16 __raw_readw(const volatile void __iomem *addr)
|
||||
static inline u16 __raw_readw_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u16 val;
|
||||
asm volatile("ldrh %0, %1"
|
||||
@@ -87,22 +89,19 @@ static inline u16 __raw_readw(const volatile void __iomem *addr)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define __raw_writeb __raw_writeb
|
||||
static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writeb_no_log(u8 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("strb %1, %0"
|
||||
: : "Qo" (*(volatile u8 __force *)addr), "r" (val));
|
||||
}
|
||||
|
||||
#define __raw_writel __raw_writel
|
||||
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writel_no_log(u32 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("str %1, %0"
|
||||
: : "Qo" (*(volatile u32 __force *)addr), "r" (val));
|
||||
}
|
||||
|
||||
#define __raw_writeq __raw_writeq
|
||||
static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writeq_no_log(u64 val, volatile void __iomem *addr)
|
||||
{
|
||||
register u64 v asm ("r2");
|
||||
|
||||
@@ -113,8 +112,7 @@ static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
|
||||
: "r" (v));
|
||||
}
|
||||
|
||||
#define __raw_readb __raw_readb
|
||||
static inline u8 __raw_readb(const volatile void __iomem *addr)
|
||||
static inline u8 __raw_readb_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u8 val;
|
||||
asm volatile("ldrb %0, %1"
|
||||
@@ -123,8 +121,7 @@ static inline u8 __raw_readb(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define __raw_readl __raw_readl
|
||||
static inline u32 __raw_readl(const volatile void __iomem *addr)
|
||||
static inline u32 __raw_readl_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u32 val;
|
||||
asm volatile("ldr %0, %1"
|
||||
@@ -133,8 +130,7 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define __raw_readq __raw_readq
|
||||
static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
static inline u64 __raw_readq_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
register u64 val asm ("r2");
|
||||
|
||||
@@ -144,6 +140,48 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* There may be cases when clients don't want to support or can't support the
|
||||
* logging. The appropriate functions can be used but clients should carefully
|
||||
* consider why they can't support the logging.
|
||||
*/
|
||||
|
||||
#define __raw_write_logged(v, a, _t) ({ \
|
||||
int _ret; \
|
||||
volatile void __iomem *_a = (a); \
|
||||
void *_addr = (void __force *)(_a); \
|
||||
_ret = uncached_logk(LOGK_WRITEL, _addr); \
|
||||
ETB_WAYPOINT; \
|
||||
__raw_write##_t##_no_log((v), _a); \
|
||||
if (_ret) \
|
||||
LOG_BARRIER; \
|
||||
})
|
||||
|
||||
|
||||
#define __raw_writeb(v, a) __raw_write_logged((v), (a), b)
|
||||
#define __raw_writew(v, a) __raw_write_logged((v), (a), w)
|
||||
#define __raw_writel(v, a) __raw_write_logged((v), (a), l)
|
||||
#define __raw_writeq(v, a) __raw_write_logged((v), (a), q)
|
||||
|
||||
#define __raw_read_logged(a, _l, _t) ({ \
|
||||
unsigned _t __a; \
|
||||
const volatile void __iomem *_a = (a); \
|
||||
void *_addr = (void __force *)(_a); \
|
||||
int _ret; \
|
||||
_ret = uncached_logk(LOGK_READL, _addr); \
|
||||
ETB_WAYPOINT; \
|
||||
__a = __raw_read##_l##_no_log(_a);\
|
||||
if (_ret) \
|
||||
LOG_BARRIER; \
|
||||
__a; \
|
||||
})
|
||||
|
||||
|
||||
#define __raw_readb(a) __raw_read_logged((a), b, char)
|
||||
#define __raw_readw(a) __raw_read_logged((a), w, short)
|
||||
#define __raw_readl(a) __raw_read_logged((a), l, int)
|
||||
#define __raw_readq(a) __raw_read_logged((a), q, long long)
|
||||
|
||||
/*
|
||||
* Architecture ioremap implementation.
|
||||
*/
|
||||
@@ -325,12 +363,24 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
|
||||
__raw_readl(c)); __r; })
|
||||
#define readq_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64) \
|
||||
__raw_readq(c)); __r; })
|
||||
#define readb_relaxed_no_log(c) ({ u8 __r = __raw_readb_no_log(c); __r; })
|
||||
#define readl_relaxed_no_log(c) ({ u32 __r = le32_to_cpu((__force __le32) \
|
||||
__raw_readl_no_log(c)); __r; })
|
||||
#define readq_relaxed_no_log(c) ({ u64 __r = le64_to_cpu((__force __le64) \
|
||||
__raw_readq_no_log(c)); __r; })
|
||||
|
||||
|
||||
#define writeb_relaxed(v, c) __raw_writeb(v, c)
|
||||
#define writew_relaxed(v, c) __raw_writew((__force u16) cpu_to_le16(v), c)
|
||||
#define writel_relaxed(v, c) __raw_writel((__force u32) cpu_to_le32(v), c)
|
||||
#define writeq_relaxed(v, c) __raw_writeq((__force u64) cpu_to_le64(v), c)
|
||||
#define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c)))
|
||||
#define writew_relaxed_no_log(v, c) __raw_writew_no_log((__force u16) \
|
||||
cpu_to_le16(v), c)
|
||||
#define writel_relaxed_no_log(v, c) __raw_writel_no_log((__force u32) \
|
||||
cpu_to_le32(v), c)
|
||||
#define writeq_relaxed_no_log(v, c) __raw_writeq_no_log((__force u64) \
|
||||
cpu_to_le64(v), c)
|
||||
|
||||
#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
|
||||
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
|
||||
@@ -351,6 +401,24 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
|
||||
#define writesw(p,d,l) __raw_writesw(p,d,l)
|
||||
#define writesl(p,d,l) __raw_writesl(p,d,l)
|
||||
|
||||
#define readb_no_log(c) \
|
||||
({ u8 __v = readb_relaxed_no_log(c); __iormb(); __v; })
|
||||
#define readw_no_log(c) \
|
||||
({ u16 __v = readw_relaxed_no_log(c); __iormb(); __v; })
|
||||
#define readl_no_log(c) \
|
||||
({ u32 __v = readl_relaxed_no_log(c); __iormb(); __v; })
|
||||
#define readq_no_log(c) \
|
||||
({ u64 __v = readq_relaxed_no_log(c); __iormb(); __v; })
|
||||
|
||||
#define writeb_no_log(v, c) \
|
||||
({ __iowmb(); writeb_relaxed_no_log((v), (c)); })
|
||||
#define writew_no_log(v, c) \
|
||||
({ __iowmb(); writew_relaxed_no_log((v), (c)); })
|
||||
#define writel_no_log(v, c) \
|
||||
({ __iowmb(); writel_relaxed_no_log((v), (c)); })
|
||||
#define writeq_no_log(v, c) \
|
||||
({ __iowmb(); writeq_relaxed_no_log((v), (c)); })
|
||||
|
||||
#ifndef __ARMBE__
|
||||
static inline void memset_io(volatile void __iomem *dst, unsigned c,
|
||||
size_t count)
|
||||
|
||||
@@ -46,21 +46,21 @@ EXPORT_SYMBOL(atomic_io_modify);
|
||||
void _memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
|
||||
{
|
||||
while (count && (!IO_CHECK_ALIGN(from, 8) || !IO_CHECK_ALIGN(to, 8))) {
|
||||
*(u8 *)to = readb_relaxed(from);
|
||||
*(u8 *)to = readb_relaxed_no_log(from);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
*(u64 *)to = readq_relaxed(from);
|
||||
*(u64 *)to = readq_relaxed_no_log(from);
|
||||
from += 8;
|
||||
to += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
*(u8 *)to = readb_relaxed(from);
|
||||
*(u8 *)to = readb_relaxed_no_log(from);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
@@ -76,21 +76,21 @@ void _memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
|
||||
void *p = (void __force *)to;
|
||||
|
||||
while (count && (!IO_CHECK_ALIGN(p, 8) || !IO_CHECK_ALIGN(from, 8))) {
|
||||
writeb_relaxed(*(volatile u8 *)from, p);
|
||||
writeb_relaxed_no_log(*(volatile u8 *)from, p);
|
||||
from++;
|
||||
p++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
writeq_relaxed(*(volatile u64 *)from, p);
|
||||
writeq_relaxed_no_log(*(volatile u64 *)from, p);
|
||||
from += 8;
|
||||
p += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
writeb_relaxed(*(volatile u8 *)from, p);
|
||||
writeb_relaxed_no_log(*(volatile u8 *)from, p);
|
||||
from++;
|
||||
p++;
|
||||
count--;
|
||||
@@ -111,19 +111,19 @@ void _memset_io(volatile void __iomem *dst, int c, size_t count)
|
||||
qc |= qc << 32;
|
||||
|
||||
while (count && !IO_CHECK_ALIGN(p, 8)) {
|
||||
writeb_relaxed(c, p);
|
||||
writeb_relaxed_no_log(c, p);
|
||||
p++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
writeq_relaxed(qc, p);
|
||||
writeq_relaxed_no_log(qc, p);
|
||||
p += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
writeb_relaxed(c, p);
|
||||
writeb_relaxed_no_log(c, p);
|
||||
p++;
|
||||
count--;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ config ARM64
|
||||
select HAVE_PERF_USER_STACK_DUMP
|
||||
select HAVE_REGS_AND_STACK_ACCESS_API
|
||||
select HAVE_RCU_TABLE_FREE
|
||||
select HAVE_RCU_TABLE_INVALIDATE
|
||||
select HAVE_SYSCALL_TRACEPOINTS
|
||||
select HAVE_KPROBES
|
||||
select HAVE_KRETPROBES
|
||||
|
||||
@@ -5524,10 +5524,11 @@ CONFIG_DAX=y
|
||||
CONFIG_NVMEM=y
|
||||
# CONFIG_QCOM_QFPROM is not set
|
||||
CONFIG_NVMEM_SPMI_SDAM=y
|
||||
# CONFIG_STM is not set
|
||||
CONFIG_STM=y
|
||||
# CONFIG_STM_DUMMY is not set
|
||||
# CONFIG_STM_SOURCE_CONSOLE is not set
|
||||
# CONFIG_STM_SOURCE_HEARTBEAT is not set
|
||||
# CONFIG_INTEL_TH is not set
|
||||
CONFIG_CORESIGHT_PLACEHOLDER=y
|
||||
CONFIG_CORESIGHT_AMBA_PLACEHOLDER=y
|
||||
# CONFIG_FPGA is not set
|
||||
|
||||
#
|
||||
@@ -5788,34 +5789,6 @@ CONFIG_TZIC_USE_QSEECOM=y
|
||||
# CONFIG_TZIC_DEFAULT is not set
|
||||
CONFIG_SPU_VERIFY=y
|
||||
|
||||
#
|
||||
# KernelSU
|
||||
#
|
||||
CONFIG_KSU=y
|
||||
# CONFIG_KSU_DEBUG is not set
|
||||
CONFIG_KSU_ALLOWLIST_WORKAROUND=y
|
||||
# CONFIG_KSU_CMDLINE is not set
|
||||
CONFIG_KSU_MANUAL_HOOK=y
|
||||
|
||||
#
|
||||
# KernelSU - SUSFS
|
||||
#
|
||||
CONFIG_KSU_SUSFS=y
|
||||
# CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT is not set
|
||||
CONFIG_KSU_SUSFS_SUS_PATH=y
|
||||
CONFIG_KSU_SUSFS_SUS_MOUNT=y
|
||||
# CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT is not set
|
||||
# CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT is not set
|
||||
# CONFIG_KSU_SUSFS_SUS_KSTAT is not set
|
||||
# CONFIG_KSU_SUSFS_SUS_OVERLAYFS is not set
|
||||
CONFIG_KSU_SUSFS_TRY_UMOUNT=y
|
||||
# CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT is not set
|
||||
CONFIG_KSU_SUSFS_SPOOF_UNAME=y
|
||||
CONFIG_KSU_SUSFS_ENABLE_LOG=y
|
||||
# CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS is not set
|
||||
# CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG is not set
|
||||
# CONFIG_KSU_SUSFS_OPEN_REDIRECT is not set
|
||||
|
||||
#
|
||||
# Firmware Drivers
|
||||
#
|
||||
@@ -5878,7 +5851,6 @@ CONFIG_F2FS_SEC_BLOCK_OPERATIONS_DEBUG=y
|
||||
CONFIG_F2FS_SEC_SUPPORT_DNODE_RELOCATION=y
|
||||
# CONFIG_FS_DAX is not set
|
||||
CONFIG_FS_POSIX_ACL=y
|
||||
CONFIG_EXPORTFS=y
|
||||
# CONFIG_EXPORTFS_BLOCK_OPS is not set
|
||||
CONFIG_FILE_LOCKING=y
|
||||
CONFIG_MANDATORY_FILE_LOCKING=y
|
||||
@@ -5904,9 +5876,7 @@ CONFIG_QUOTACTL=y
|
||||
CONFIG_FUSE_FS=y
|
||||
# CONFIG_CUSE is not set
|
||||
CONFIG_FUSE_SUPPORT_STLOG=y
|
||||
CONFIG_OVERLAY_FS=y
|
||||
CONFIG_OVERLAY_FS_REDIRECT_DIR=y
|
||||
CONFIG_OVERLAY_FS_INDEX=y
|
||||
# CONFIG_OVERLAY_FS is not set
|
||||
# CONFIG_INCREMENTAL_FS is not set
|
||||
|
||||
#
|
||||
@@ -6221,6 +6191,8 @@ CONFIG_RING_BUFFER=y
|
||||
CONFIG_EVENT_TRACING=y
|
||||
CONFIG_CONTEXT_SWITCH_TRACER=y
|
||||
CONFIG_IPC_LOGGING=y
|
||||
CONFIG_QCOM_RTB=y
|
||||
CONFIG_QCOM_RTB_SEPARATE_CPUS=y
|
||||
CONFIG_TRACING=y
|
||||
CONFIG_GENERIC_TRACER=y
|
||||
CONFIG_TRACING_SUPPORT=y
|
||||
@@ -6302,7 +6274,30 @@ CONFIG_DEBUG_ALIGN_RODATA=y
|
||||
#
|
||||
CONFIG_SEC_PM=y
|
||||
CONFIG_SEC_PM_DEBUG=y
|
||||
# CONFIG_CORESIGHT is not set
|
||||
CONFIG_CORESIGHT=y
|
||||
CONFIG_CORESIGHT_LINKS_AND_SINKS=y
|
||||
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
|
||||
# CONFIG_CORESIGHT_CATU is not set
|
||||
# CONFIG_CORESIGHT_SINK_TPIU is not set
|
||||
# CONFIG_CORESIGHT_SINK_ETBV10 is not set
|
||||
# CONFIG_CORESIGHT_SOURCE_ETM4X is not set
|
||||
CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y
|
||||
# CONFIG_CORESIGHT_DBGUI is not set
|
||||
CONFIG_CORESIGHT_STM=y
|
||||
# CONFIG_CORESIGHT_CPU_DEBUG is not set
|
||||
CONFIG_CORESIGHT_CTI=y
|
||||
CONFIG_CORESIGHT_OST=y
|
||||
CONFIG_CORESIGHT_TPDA=y
|
||||
CONFIG_CORESIGHT_TPDM=y
|
||||
# CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE is not set
|
||||
# CONFIG_CORESIGHT_QPDI is not set
|
||||
CONFIG_CORESIGHT_HWEVENT=y
|
||||
CONFIG_CORESIGHT_DUMMY=y
|
||||
CONFIG_CORESIGHT_REMOTE_ETM=y
|
||||
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
|
||||
CONFIG_CORESIGHT_CSR=y
|
||||
# CONFIG_CORESIGHT_TGU is not set
|
||||
CONFIG_CORESIGHT_EVENT=y
|
||||
|
||||
#
|
||||
# Security options
|
||||
|
||||
@@ -120,8 +120,8 @@ static inline void gic_write_bpr1(u32 val)
|
||||
write_sysreg_s(val, SYS_ICC_BPR1_EL1);
|
||||
}
|
||||
|
||||
#define gic_read_typer(c) readq_relaxed(c)
|
||||
#define gic_write_irouter(v, c) writeq_relaxed(v, c)
|
||||
#define gic_read_typer(c) readq_relaxed_no_log(c)
|
||||
#define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c)
|
||||
#define gic_read_lpir(c) readq_relaxed(c)
|
||||
#define gic_write_lpir(v, c) writeq_relaxed(v, c)
|
||||
|
||||
|
||||
@@ -30,38 +30,35 @@
|
||||
#include <asm/early_ioremap.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <linux/msm_rtb.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
|
||||
/*
|
||||
* Generic IO read/write. These perform native-endian accesses.
|
||||
* that some architectures will want to re-define __raw_{read,write}w.
|
||||
*/
|
||||
#define __raw_writeb __raw_writeb
|
||||
static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writeb_no_log(u8 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("strb %w0, [%1]" : : "rZ" (val), "r" (addr));
|
||||
}
|
||||
|
||||
#define __raw_writew __raw_writew
|
||||
static inline void __raw_writew(u16 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writew_no_log(u16 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("strh %w0, [%1]" : : "rZ" (val), "r" (addr));
|
||||
}
|
||||
|
||||
#define __raw_writel __raw_writel
|
||||
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writel_no_log(u32 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));
|
||||
}
|
||||
|
||||
#define __raw_writeq __raw_writeq
|
||||
static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
|
||||
static inline void __raw_writeq_no_log(u64 val, volatile void __iomem *addr)
|
||||
{
|
||||
asm volatile("str %x0, [%1]" : : "rZ" (val), "r" (addr));
|
||||
}
|
||||
|
||||
#define __raw_readb __raw_readb
|
||||
static inline u8 __raw_readb(const volatile void __iomem *addr)
|
||||
static inline u8 __raw_readb_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u8 val;
|
||||
asm volatile(ALTERNATIVE("ldrb %w0, [%1]",
|
||||
@@ -71,8 +68,7 @@ static inline u8 __raw_readb(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define __raw_readw __raw_readw
|
||||
static inline u16 __raw_readw(const volatile void __iomem *addr)
|
||||
static inline u16 __raw_readw_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
@@ -83,8 +79,7 @@ static inline u16 __raw_readw(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define __raw_readl __raw_readl
|
||||
static inline u32 __raw_readl(const volatile void __iomem *addr)
|
||||
static inline u32 __raw_readl_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u32 val;
|
||||
asm volatile(ALTERNATIVE("ldr %w0, [%1]",
|
||||
@@ -94,8 +89,7 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define __raw_readq __raw_readq
|
||||
static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
static inline u64 __raw_readq_no_log(const volatile void __iomem *addr)
|
||||
{
|
||||
u64 val;
|
||||
asm volatile(ALTERNATIVE("ldr %0, [%1]",
|
||||
@@ -105,6 +99,48 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* There may be cases when clients don't want to support or can't support the
|
||||
* logging, The appropriate functions can be used but clinets should carefully
|
||||
* consider why they can't support the logging
|
||||
*/
|
||||
|
||||
#define __raw_write_logged(v, a, _t) ({ \
|
||||
int _ret; \
|
||||
volatile void __iomem *_a = (a); \
|
||||
void *_addr = (void __force *)(_a); \
|
||||
_ret = uncached_logk(LOGK_WRITEL, _addr); \
|
||||
if (_ret) /* COFNIG_SEC_DEBUG */\
|
||||
ETB_WAYPOINT; \
|
||||
__raw_write##_t##_no_log((v), _a); \
|
||||
if (_ret) \
|
||||
LOG_BARRIER; \
|
||||
})
|
||||
|
||||
#define __raw_writeb(v, a) __raw_write_logged((v), a, b)
|
||||
#define __raw_writew(v, a) __raw_write_logged((v), a, w)
|
||||
#define __raw_writel(v, a) __raw_write_logged((v), a, l)
|
||||
#define __raw_writeq(v, a) __raw_write_logged((v), a, q)
|
||||
|
||||
#define __raw_read_logged(a, _l, _t) ({ \
|
||||
_t __a; \
|
||||
const volatile void __iomem *_a = (a); \
|
||||
void *_addr = (void __force *)(_a); \
|
||||
int _ret; \
|
||||
_ret = uncached_logk(LOGK_READL, _addr); \
|
||||
if (_ret) /* CONFIG_SEC_DEBUG */ \
|
||||
ETB_WAYPOINT; \
|
||||
__a = __raw_read##_l##_no_log(_a); \
|
||||
if (_ret) \
|
||||
LOG_BARRIER; \
|
||||
__a; \
|
||||
})
|
||||
|
||||
#define __raw_readb(a) __raw_read_logged((a), b, u8)
|
||||
#define __raw_readw(a) __raw_read_logged((a), w, u16)
|
||||
#define __raw_readl(a) __raw_read_logged((a), l, u32)
|
||||
#define __raw_readq(a) __raw_read_logged((a), q, u64)
|
||||
|
||||
/* IO barriers */
|
||||
#define __iormb(v) \
|
||||
({ \
|
||||
@@ -142,6 +178,22 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
#define writel_relaxed(v,c) ((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
|
||||
#define writeq_relaxed(v,c) ((void)__raw_writeq((__force u64)cpu_to_le64(v),(c)))
|
||||
|
||||
#define readb_relaxed_no_log(c) ({ u8 __v = __raw_readb_no_log(c); __v; })
|
||||
#define readw_relaxed_no_log(c) \
|
||||
({ u16 __v = le16_to_cpu((__force __le16)__raw_readw_no_log(c)); __v; })
|
||||
#define readl_relaxed_no_log(c) \
|
||||
({ u32 __v = le32_to_cpu((__force __le32)__raw_readl_no_log(c)); __v; })
|
||||
#define readq_relaxed_no_log(c) \
|
||||
({ u64 __v = le64_to_cpu((__force __le64)__raw_readq_no_log(c)); __v; })
|
||||
|
||||
#define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c)))
|
||||
#define writew_relaxed_no_log(v, c) \
|
||||
((void)__raw_writew_no_log((__force u16)cpu_to_le32(v), (c)))
|
||||
#define writel_relaxed_no_log(v, c) \
|
||||
((void)__raw_writel_no_log((__force u32)cpu_to_le32(v), (c)))
|
||||
#define writeq_relaxed_no_log(v, c) \
|
||||
((void)__raw_writeq_no_log((__force u64)cpu_to_le32(v), (c)))
|
||||
|
||||
/*
|
||||
* I/O memory access primitives. Reads are ordered relative to any
|
||||
* following Normal memory access. Writes are ordered relative to any prior
|
||||
@@ -157,6 +209,24 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
||||
#define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c)); })
|
||||
#define writeq(v,c) ({ __iowmb(); writeq_relaxed((v),(c)); })
|
||||
|
||||
#define readb_no_log(c) \
|
||||
({ u8 __v = readb_relaxed_no_log(c); __iormb(__v); __v; })
|
||||
#define readw_no_log(c) \
|
||||
({ u16 __v = readw_relaxed_no_log(c); __iormb(__v); __v; })
|
||||
#define readl_no_log(c) \
|
||||
({ u32 __v = readl_relaxed_no_log(c); __iormb(__v); __v; })
|
||||
#define readq_no_log(c) \
|
||||
({ u64 __v = readq_relaxed_no_log(c); __iormb(__v); __v; })
|
||||
|
||||
#define writeb_no_log(v, c) \
|
||||
({ __iowmb(); writeb_relaxed_no_log((v), (c)); })
|
||||
#define writew_no_log(v, c) \
|
||||
({ __iowmb(); writew_relaxed_no_log((v), (c)); })
|
||||
#define writel_no_log(v, c) \
|
||||
({ __iowmb(); writel_relaxed_no_log((v), (c)); })
|
||||
#define writeq_no_log(v, c) \
|
||||
({ __iowmb(); writeq_relaxed_no_log((v), (c)); })
|
||||
|
||||
/*
|
||||
* I/O port access primitives.
|
||||
*/
|
||||
|
||||
@@ -34,14 +34,19 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <linux/msm_rtb.h>
|
||||
|
||||
static inline void contextidr_thread_switch(struct task_struct *next)
|
||||
{
|
||||
pid_t pid = task_pid_nr(next);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR))
|
||||
return;
|
||||
|
||||
write_sysreg(task_pid_nr(next), contextidr_el1);
|
||||
write_sysreg(pid, contextidr_el1);
|
||||
isb();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -381,7 +381,6 @@ static inline int pmd_protnone(pmd_t pmd)
|
||||
#define pmd_present(pmd) pte_present(pmd_pte(pmd))
|
||||
#define pmd_dirty(pmd) pte_dirty(pmd_pte(pmd))
|
||||
#define pmd_young(pmd) pte_young(pmd_pte(pmd))
|
||||
#define pmd_valid(pmd) pte_valid(pmd_pte(pmd))
|
||||
#define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd)))
|
||||
#define pmd_mkold(pmd) pte_pmd(pte_mkold(pmd_pte(pmd)))
|
||||
#define pmd_mkwrite(pmd) pte_pmd(pte_mkwrite(pmd_pte(pmd)))
|
||||
@@ -460,11 +459,8 @@ static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
|
||||
#else
|
||||
*pmdp = pmd;
|
||||
#endif
|
||||
|
||||
if (pmd_valid(pmd)) {
|
||||
dsb(ishst);
|
||||
isb();
|
||||
}
|
||||
dsb(ishst);
|
||||
isb();
|
||||
}
|
||||
|
||||
static inline void pmd_clear(pmd_t *pmdp)
|
||||
@@ -516,7 +512,6 @@ static inline void pte_unmap(pte_t *pte) { }
|
||||
#define pud_none(pud) (!pud_val(pud))
|
||||
#define pud_bad(pud) (!(pud_val(pud) & PUD_TABLE_BIT))
|
||||
#define pud_present(pud) pte_present(pud_pte(pud))
|
||||
#define pud_valid(pud) pte_valid(pud_pte(pud))
|
||||
|
||||
static inline void set_pud(pud_t *pudp, pud_t pud)
|
||||
{
|
||||
@@ -534,11 +529,8 @@ static inline void set_pud(pud_t *pudp, pud_t pud)
|
||||
#else
|
||||
*pudp = pud;
|
||||
#endif
|
||||
|
||||
if (pud_valid(pud)) {
|
||||
dsb(ishst);
|
||||
isb();
|
||||
}
|
||||
dsb(ishst);
|
||||
isb();
|
||||
}
|
||||
|
||||
static inline void pud_clear(pud_t *pudp)
|
||||
|
||||
@@ -25,40 +25,44 @@
|
||||
#include <linux/rkp.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
|
||||
#define tlb_remove_entry(tlb, entry) tlb_remove_table(tlb, entry)
|
||||
static inline void __tlb_remove_table(void *_table)
|
||||
{
|
||||
free_page_and_swap_cache((struct page *)_table);
|
||||
}
|
||||
|
||||
static void tlb_flush(struct mmu_gather *tlb);
|
||||
#else
|
||||
#define tlb_remove_entry(tlb, entry) tlb_remove_page(tlb, entry)
|
||||
#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
|
||||
|
||||
#include <asm-generic/tlb.h>
|
||||
|
||||
static inline void tlb_flush(struct mmu_gather *tlb)
|
||||
{
|
||||
struct vm_area_struct vma = { .vm_mm = tlb->mm, };
|
||||
bool last_level = !tlb->freed_tables;
|
||||
unsigned long stride = tlb_get_unmap_size(tlb);
|
||||
|
||||
/*
|
||||
* If we're tearing down the address space then we only care about
|
||||
* invalidating the walk-cache, since the ASID allocator won't
|
||||
* reallocate our ASID without invalidating the entire TLB.
|
||||
* The ASID allocator will either invalidate the ASID or mark
|
||||
* it as used.
|
||||
*/
|
||||
if (tlb->fullmm) {
|
||||
if (!last_level)
|
||||
flush_tlb_mm(tlb->mm);
|
||||
if (tlb->fullmm)
|
||||
return;
|
||||
}
|
||||
|
||||
__flush_tlb_range(&vma, tlb->start, tlb->end, stride, last_level);
|
||||
/*
|
||||
* The intermediate page table levels are already handled by
|
||||
* the __(pte|pmd|pud)_free_tlb() functions, so last level
|
||||
* TLBI is sufficient here.
|
||||
*/
|
||||
__flush_tlb_range(&vma, tlb->start, tlb->end, true);
|
||||
}
|
||||
|
||||
static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
||||
unsigned long addr)
|
||||
{
|
||||
__flush_tlb_pgtable(tlb->mm, addr);
|
||||
pgtable_page_dtor(pte);
|
||||
tlb_remove_table(tlb, pte);
|
||||
tlb_remove_entry(tlb, pte);
|
||||
}
|
||||
|
||||
#if CONFIG_PGTABLE_LEVELS > 2
|
||||
@@ -70,7 +74,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
|
||||
rkp_ro_free((void *)pmdp);
|
||||
} else
|
||||
#endif
|
||||
tlb_remove_table(tlb, virt_to_page(pmdp));
|
||||
tlb_remove_entry(tlb, virt_to_page(pmdp));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -83,7 +87,7 @@ static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
|
||||
rkp_ro_free((void *)pudp);
|
||||
else
|
||||
#endif
|
||||
tlb_remove_table(tlb, virt_to_page(pudp));
|
||||
tlb_remove_entry(tlb, virt_to_page(pudp));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -70,73 +70,43 @@
|
||||
})
|
||||
|
||||
/*
|
||||
* TLB Invalidation
|
||||
* ================
|
||||
* TLB Management
|
||||
* ==============
|
||||
*
|
||||
* This header file implements the low-level TLB invalidation routines
|
||||
* (sometimes referred to as "flushing" in the kernel) for arm64.
|
||||
*
|
||||
* Every invalidation operation uses the following template:
|
||||
*
|
||||
* DSB ISHST // Ensure prior page-table updates have completed
|
||||
* TLBI ... // Invalidate the TLB
|
||||
* DSB ISH // Ensure the TLB invalidation has completed
|
||||
* if (invalidated kernel mappings)
|
||||
* ISB // Discard any instructions fetched from the old mapping
|
||||
*
|
||||
*
|
||||
* The following functions form part of the "core" TLB invalidation API,
|
||||
* as documented in Documentation/core-api/cachetlb.rst:
|
||||
* The TLB specific code is expected to perform whatever tests it needs
|
||||
* to determine if it should invalidate the TLB for each call. Start
|
||||
* addresses are inclusive and end addresses are exclusive; it is safe to
|
||||
* round these addresses down.
|
||||
*
|
||||
* flush_tlb_all()
|
||||
* Invalidate the entire TLB (kernel + user) on all CPUs
|
||||
*
|
||||
* Invalidate the entire TLB.
|
||||
*
|
||||
* flush_tlb_mm(mm)
|
||||
* Invalidate an entire user address space on all CPUs.
|
||||
* The 'mm' argument identifies the ASID to invalidate.
|
||||
*
|
||||
* flush_tlb_range(vma, start, end)
|
||||
* Invalidate the virtual-address range '[start, end)' on all
|
||||
* CPUs for the user address space corresponding to 'vma->mm'.
|
||||
* Note that this operation also invalidates any walk-cache
|
||||
* entries associated with translations for the specified address
|
||||
* range.
|
||||
* Invalidate all TLB entries in a particular address space.
|
||||
* - mm - mm_struct describing address space
|
||||
*
|
||||
* flush_tlb_kernel_range(start, end)
|
||||
* Same as flush_tlb_range(..., start, end), but applies to
|
||||
* kernel mappings rather than a particular user address space.
|
||||
* Whilst not explicitly documented, this function is used when
|
||||
* unmapping pages from vmalloc/io space.
|
||||
* flush_tlb_range(mm,start,end)
|
||||
*
|
||||
* flush_tlb_page(vma, addr)
|
||||
* Invalidate a single user mapping for address 'addr' in the
|
||||
* address space corresponding to 'vma->mm'. Note that this
|
||||
* operation only invalidates a single, last-level page-table
|
||||
* entry and therefore does not affect any walk-caches.
|
||||
* Invalidate a range of TLB entries in the specified address
|
||||
* space.
|
||||
* - mm - mm_struct describing address space
|
||||
* - start - start address (may not be aligned)
|
||||
* - end - end address (exclusive, may not be aligned)
|
||||
*
|
||||
* flush_tlb_page(vaddr,vma)
|
||||
*
|
||||
* Next, we have some undocumented invalidation routines that you probably
|
||||
* don't want to call unless you know what you're doing:
|
||||
* Invalidate the specified page in the specified address range.
|
||||
* - vaddr - virtual address (may not be aligned)
|
||||
* - vma - vma_struct describing address range
|
||||
*
|
||||
* local_flush_tlb_all()
|
||||
* Same as flush_tlb_all(), but only applies to the calling CPU.
|
||||
* flush_kern_tlb_page(kaddr)
|
||||
*
|
||||
* __flush_tlb_kernel_pgtable(addr)
|
||||
* Invalidate a single kernel mapping for address 'addr' on all
|
||||
* CPUs, ensuring that any walk-cache entries associated with the
|
||||
* translation are also invalidated.
|
||||
*
|
||||
* __flush_tlb_range(vma, start, end, stride, last_level)
|
||||
* Invalidate the virtual-address range '[start, end)' on all
|
||||
* CPUs for the user address space corresponding to 'vma->mm'.
|
||||
* The invalidation operations are issued at a granularity
|
||||
* determined by 'stride' and only affect any walk-cache entries
|
||||
* if 'last_level' is equal to false.
|
||||
*
|
||||
*
|
||||
* Finally, take a look at asm/tlb.h to see how tlb_flush() is implemented
|
||||
* on top of these routines, since that is our interface to the mmu_gather
|
||||
* API as used by munmap() and friends.
|
||||
* Invalidate the TLB entry for the specified page. The address
|
||||
* will be in the kernels virtual memory space. Current uses
|
||||
* only require the D-TLB to be invalidated.
|
||||
* - kaddr - Kernel virtual memory address
|
||||
*/
|
||||
static inline void local_flush_tlb_all(void)
|
||||
{
|
||||
@@ -179,28 +149,25 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
|
||||
* This is meant to avoid soft lock-ups on large TLB flushing ranges and not
|
||||
* necessarily a performance improvement.
|
||||
*/
|
||||
#define MAX_TLBI_OPS 1024UL
|
||||
#define MAX_TLB_RANGE (1024UL << PAGE_SHIFT)
|
||||
|
||||
static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end,
|
||||
unsigned long stride, bool last_level)
|
||||
bool last_level)
|
||||
{
|
||||
unsigned long asid = ASID(vma->vm_mm);
|
||||
unsigned long addr;
|
||||
|
||||
if ((end - start) > (MAX_TLBI_OPS * stride)) {
|
||||
if ((end - start) > MAX_TLB_RANGE) {
|
||||
flush_tlb_mm(vma->vm_mm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert the stride into units of 4k */
|
||||
stride >>= 12;
|
||||
|
||||
start = __TLBI_VADDR(start, asid);
|
||||
end = __TLBI_VADDR(end, asid);
|
||||
|
||||
dsb(ishst);
|
||||
for (addr = start; addr < end; addr += stride) {
|
||||
for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) {
|
||||
if (last_level) {
|
||||
__tlbi(vale1is, addr);
|
||||
__tlbi_user(vale1is, addr);
|
||||
@@ -215,18 +182,14 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
||||
static inline void flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
/*
|
||||
* We cannot use leaf-only invalidation here, since we may be invalidating
|
||||
* table entries as part of collapsing hugepages or moving page tables.
|
||||
*/
|
||||
__flush_tlb_range(vma, start, end, PAGE_SIZE, false);
|
||||
__flush_tlb_range(vma, start, end, false);
|
||||
}
|
||||
|
||||
static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
if ((end - start) > (MAX_TLBI_OPS * PAGE_SIZE)) {
|
||||
if ((end - start) > MAX_TLB_RANGE) {
|
||||
flush_tlb_all();
|
||||
return;
|
||||
}
|
||||
@@ -236,7 +199,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
|
||||
|
||||
dsb(ishst);
|
||||
for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12))
|
||||
__tlbi(vaale1is, addr);
|
||||
__tlbi(vaae1is, addr);
|
||||
dsb(ish);
|
||||
isb();
|
||||
}
|
||||
@@ -245,11 +208,20 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
|
||||
* Used to invalidate the TLB (walk caches) corresponding to intermediate page
|
||||
* table levels (pgd/pud/pmd).
|
||||
*/
|
||||
static inline void __flush_tlb_pgtable(struct mm_struct *mm,
|
||||
unsigned long uaddr)
|
||||
{
|
||||
unsigned long addr = __TLBI_VADDR(uaddr, ASID(mm));
|
||||
|
||||
__tlbi(vae1is, addr);
|
||||
__tlbi_user(vae1is, addr);
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)
|
||||
{
|
||||
unsigned long addr = __TLBI_VADDR(kaddr, 0);
|
||||
|
||||
dsb(ishst);
|
||||
__tlbi(vaae1is, addr);
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
@@ -27,21 +27,21 @@ void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
|
||||
{
|
||||
while (count && (!IS_ALIGNED((unsigned long)from, 8) ||
|
||||
!IS_ALIGNED((unsigned long)to, 8))) {
|
||||
*(u8 *)to = __raw_readb(from);
|
||||
*(u8 *)to = __raw_readb_no_log(from);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
*(u64 *)to = __raw_readq(from);
|
||||
*(u64 *)to = __raw_readq_no_log(from);
|
||||
from += 8;
|
||||
to += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
*(u8 *)to = __raw_readb(from);
|
||||
*(u8 *)to = __raw_readb_no_log(from);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
@@ -56,21 +56,21 @@ void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
|
||||
{
|
||||
while (count && (!IS_ALIGNED((unsigned long)to, 8) ||
|
||||
!IS_ALIGNED((unsigned long)from, 8))) {
|
||||
__raw_writeb(*(volatile u8 *)from, to);
|
||||
__raw_writeb_no_log(*(volatile u8 *)from, to);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
__raw_writeq(*(volatile u64 *)from, to);
|
||||
__raw_writeq_no_log(*(volatile u64 *)from, to);
|
||||
from += 8;
|
||||
to += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
__raw_writeb(*(volatile u8 *)from, to);
|
||||
__raw_writeb_no_log(*(volatile u8 *)from, to);
|
||||
from++;
|
||||
to++;
|
||||
count--;
|
||||
@@ -90,19 +90,19 @@ void __memset_io(volatile void __iomem *dst, int c, size_t count)
|
||||
qc |= qc << 32;
|
||||
|
||||
while (count && !IS_ALIGNED((unsigned long)dst, 8)) {
|
||||
__raw_writeb(c, dst);
|
||||
__raw_writeb_no_log(c, dst);
|
||||
dst++;
|
||||
count--;
|
||||
}
|
||||
|
||||
while (count >= 8) {
|
||||
__raw_writeq(qc, dst);
|
||||
__raw_writeq_no_log(qc, dst);
|
||||
dst += 8;
|
||||
count -= 8;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
__raw_writeb(c, dst);
|
||||
__raw_writeb_no_log(c, dst);
|
||||
dst++;
|
||||
count--;
|
||||
}
|
||||
|
||||
@@ -221,8 +221,6 @@ source "drivers/hwtracing/stm/Kconfig"
|
||||
|
||||
source "drivers/hwtracing/intel_th/Kconfig"
|
||||
|
||||
source "drivers/hwtracing/google/Kconfig"
|
||||
|
||||
source "drivers/fpga/Kconfig"
|
||||
|
||||
source "drivers/fsi/Kconfig"
|
||||
@@ -261,6 +259,4 @@ source "drivers/security/samsung/tzic/Kconfig"
|
||||
|
||||
source "drivers/spu_verify/Kconfig"
|
||||
|
||||
source "drivers/kernelsu/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -187,7 +187,6 @@ obj-$(CONFIG_RAS) += ras/
|
||||
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
|
||||
obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
|
||||
obj-y += hwtracing/intel_th/
|
||||
obj-y += hwtracing/google/
|
||||
obj-$(CONFIG_STM) += hwtracing/stm/
|
||||
obj-$(CONFIG_ANDROID) += android/
|
||||
obj-$(CONFIG_NVMEM) += nvmem/
|
||||
@@ -228,5 +227,3 @@ obj-$(CONFIG_TZIC) += security/samsung/tzic/
|
||||
|
||||
# SPU signature verify
|
||||
obj-$(CONFIG_SPU_VERIFY) += spu_verify/
|
||||
|
||||
obj-$(CONFIG_KSU) += kernelsu/
|
||||
|
||||
@@ -861,7 +861,7 @@ static inline void mhi_timesync_log(struct mhi_controller *mhi_cntrl)
|
||||
|
||||
if (mhi_tsync && mhi_cntrl->tsync_log)
|
||||
mhi_cntrl->tsync_log(mhi_cntrl,
|
||||
readq(mhi_tsync->time_reg));
|
||||
readq_no_log(mhi_tsync->time_reg));
|
||||
}
|
||||
|
||||
/* memory allocation methods */
|
||||
|
||||
@@ -2621,7 +2621,7 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
|
||||
local_irq_disable();
|
||||
|
||||
*t_host = mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data);
|
||||
*t_dev = readq_relaxed(mhi_tsync->time_reg);
|
||||
*t_dev = readq_relaxed_no_log(mhi_tsync->time_reg);
|
||||
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
@@ -2726,7 +2726,7 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
|
||||
|
||||
mhi_tsync->local_time =
|
||||
mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data);
|
||||
writel_relaxed(mhi_tsync->int_sequence, mhi_cntrl->tsync_db);
|
||||
writel_relaxed_no_log(mhi_tsync->int_sequence, mhi_cntrl->tsync_db);
|
||||
/* write must go thru immediately */
|
||||
wmb();
|
||||
|
||||
|
||||
@@ -596,44 +596,64 @@ static void fastrpc_remote_buf_list_free(struct fastrpc_file *fl)
|
||||
} while (free);
|
||||
}
|
||||
|
||||
static void fastrpc_mmap_add_global(struct fastrpc_mmap *map)
|
||||
{
|
||||
struct fastrpc_apps *me = &gfa;
|
||||
unsigned long irq_flags = 0;
|
||||
|
||||
spin_lock_irqsave(&me->hlock, irq_flags);
|
||||
hlist_add_head(&map->hn, &me->maps);
|
||||
spin_unlock_irqrestore(&me->hlock, irq_flags);
|
||||
}
|
||||
|
||||
static void fastrpc_mmap_add(struct fastrpc_mmap *map)
|
||||
{
|
||||
struct fastrpc_file *fl = map->fl;
|
||||
if (map->flags == ADSP_MMAP_HEAP_ADDR ||
|
||||
map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
|
||||
struct fastrpc_apps *me = &gfa;
|
||||
|
||||
hlist_add_head(&map->hn, &fl->maps);
|
||||
spin_lock(&me->hlock);
|
||||
hlist_add_head(&map->hn, &me->maps);
|
||||
spin_unlock(&me->hlock);
|
||||
} else {
|
||||
struct fastrpc_file *fl = map->fl;
|
||||
|
||||
hlist_add_head(&map->hn, &fl->maps);
|
||||
}
|
||||
}
|
||||
|
||||
static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd,
|
||||
uintptr_t va, size_t len, int mflags, int refs,
|
||||
struct fastrpc_mmap **ppmap)
|
||||
{
|
||||
struct fastrpc_apps *me = &gfa;
|
||||
struct fastrpc_mmap *match = NULL, *map = NULL;
|
||||
struct hlist_node *n;
|
||||
|
||||
if ((va + len) < va)
|
||||
return -EOVERFLOW;
|
||||
|
||||
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
|
||||
if (va >= map->va &&
|
||||
va + len <= map->va + map->len &&
|
||||
map->fd == fd) {
|
||||
if (refs) {
|
||||
if (map->refs + 1 == INT_MAX)
|
||||
return -ETOOMANYREFS;
|
||||
map->refs++;
|
||||
if (mflags == ADSP_MMAP_HEAP_ADDR ||
|
||||
mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
|
||||
spin_lock(&me->hlock);
|
||||
hlist_for_each_entry_safe(map, n, &me->maps, hn) {
|
||||
if (va >= map->va &&
|
||||
va + len <= map->va + map->len &&
|
||||
map->fd == fd) {
|
||||
if (refs) {
|
||||
if (map->refs + 1 == INT_MAX) {
|
||||
spin_unlock(&me->hlock);
|
||||
return -ETOOMANYREFS;
|
||||
}
|
||||
map->refs++;
|
||||
}
|
||||
match = map;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&me->hlock);
|
||||
} else {
|
||||
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
|
||||
if (va >= map->va &&
|
||||
va + len <= map->va + map->len &&
|
||||
map->fd == fd) {
|
||||
if (refs) {
|
||||
if (map->refs + 1 == INT_MAX)
|
||||
return -ETOOMANYREFS;
|
||||
map->refs++;
|
||||
}
|
||||
match = map;
|
||||
break;
|
||||
}
|
||||
match = map;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
@@ -977,9 +997,8 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
|
||||
map->va = va;
|
||||
}
|
||||
map->len = len;
|
||||
if ((mflags != ADSP_MMAP_HEAP_ADDR) &&
|
||||
(mflags != ADSP_MMAP_REMOTE_HEAP_ADDR))
|
||||
fastrpc_mmap_add(map);
|
||||
|
||||
fastrpc_mmap_add(map);
|
||||
*ppmap = map;
|
||||
|
||||
bail:
|
||||
@@ -2292,7 +2311,6 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
|
||||
mutex_unlock(&fl->map_mutex);
|
||||
if (err)
|
||||
goto bail;
|
||||
fastrpc_mmap_add_global(mem);
|
||||
phys = mem->phys;
|
||||
size = mem->size;
|
||||
if (me->channel[fl->cid].rhvm.vmid) {
|
||||
@@ -2623,7 +2641,7 @@ static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl)
|
||||
} while (match);
|
||||
bail:
|
||||
if (err && match)
|
||||
fastrpc_mmap_add_global(match);
|
||||
fastrpc_mmap_add(match);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2740,11 +2758,7 @@ static int fastrpc_internal_munmap(struct fastrpc_file *fl,
|
||||
bail:
|
||||
if (err && map) {
|
||||
mutex_lock(&fl->map_mutex);
|
||||
if ((map->flags == ADSP_MMAP_HEAP_ADDR) ||
|
||||
(map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR))
|
||||
fastrpc_mmap_add_global(map);
|
||||
else
|
||||
fastrpc_mmap_add(map);
|
||||
fastrpc_mmap_add(map);
|
||||
mutex_unlock(&fl->map_mutex);
|
||||
}
|
||||
mutex_unlock(&fl->internal_map_mutex);
|
||||
@@ -2851,9 +2865,6 @@ static int fastrpc_internal_mmap(struct fastrpc_file *fl,
|
||||
if (err)
|
||||
goto bail;
|
||||
map->raddr = raddr;
|
||||
if (ud->flags == ADSP_MMAP_HEAP_ADDR ||
|
||||
ud->flags == ADSP_MMAP_REMOTE_HEAP_ADDR)
|
||||
fastrpc_mmap_add_global(map);
|
||||
}
|
||||
ud->vaddrout = raddr;
|
||||
bail:
|
||||
|
||||
@@ -110,9 +110,14 @@ static inline int clk_osm_read_reg(struct clk_osm *c, u32 offset)
|
||||
return readl_relaxed(c->vbase + offset);
|
||||
}
|
||||
|
||||
static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset)
|
||||
{
|
||||
return readl_relaxed_no_log(c->vbase + offset);
|
||||
}
|
||||
|
||||
static inline int clk_osm_mb(struct clk_osm *c)
|
||||
{
|
||||
return readl_relaxed(c->vbase + ENABLE_REG);
|
||||
return readl_relaxed_no_log(c->vbase + ENABLE_REG);
|
||||
}
|
||||
|
||||
static long clk_osm_list_rate(struct clk_hw *hw, unsigned int n,
|
||||
@@ -919,7 +924,7 @@ static u64 clk_osm_get_cpu_cycle_counter(int cpu)
|
||||
* core DCVS is disabled.
|
||||
*/
|
||||
core_num = parent->per_core_dcvs ? c->core_num : 0;
|
||||
val = clk_osm_read_reg(parent,
|
||||
val = clk_osm_read_reg_no_log(parent,
|
||||
OSM_CYCLE_COUNTER_STATUS_REG(core_num));
|
||||
|
||||
if (val < c->prev_cycle_counter) {
|
||||
|
||||
@@ -100,20 +100,20 @@ void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
|
||||
struct arch_timer *timer = to_arch_timer(clk);
|
||||
switch (reg) {
|
||||
case ARCH_TIMER_REG_CTRL:
|
||||
writel_relaxed(val, timer->base + CNTP_CTL);
|
||||
writel_relaxed_no_log(val, timer->base + CNTP_CTL);
|
||||
break;
|
||||
case ARCH_TIMER_REG_TVAL:
|
||||
writel_relaxed(val, timer->base + CNTP_TVAL);
|
||||
writel_relaxed_no_log(val, timer->base + CNTP_TVAL);
|
||||
break;
|
||||
}
|
||||
} else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) {
|
||||
struct arch_timer *timer = to_arch_timer(clk);
|
||||
switch (reg) {
|
||||
case ARCH_TIMER_REG_CTRL:
|
||||
writel_relaxed(val, timer->base + CNTV_CTL);
|
||||
writel_relaxed_no_log(val, timer->base + CNTV_CTL);
|
||||
break;
|
||||
case ARCH_TIMER_REG_TVAL:
|
||||
writel_relaxed(val, timer->base + CNTV_TVAL);
|
||||
writel_relaxed_no_log(val, timer->base + CNTV_TVAL);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -131,20 +131,20 @@ u32 arch_timer_reg_read(int access, enum arch_timer_reg reg,
|
||||
struct arch_timer *timer = to_arch_timer(clk);
|
||||
switch (reg) {
|
||||
case ARCH_TIMER_REG_CTRL:
|
||||
val = readl_relaxed(timer->base + CNTP_CTL);
|
||||
val = readl_relaxed_no_log(timer->base + CNTP_CTL);
|
||||
break;
|
||||
case ARCH_TIMER_REG_TVAL:
|
||||
val = readl_relaxed(timer->base + CNTP_TVAL);
|
||||
val = readl_relaxed_no_log(timer->base + CNTP_TVAL);
|
||||
break;
|
||||
}
|
||||
} else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) {
|
||||
struct arch_timer *timer = to_arch_timer(clk);
|
||||
switch (reg) {
|
||||
case ARCH_TIMER_REG_CTRL:
|
||||
val = readl_relaxed(timer->base + CNTV_CTL);
|
||||
val = readl_relaxed_no_log(timer->base + CNTV_CTL);
|
||||
break;
|
||||
case ARCH_TIMER_REG_TVAL:
|
||||
val = readl_relaxed(timer->base + CNTV_TVAL);
|
||||
val = readl_relaxed_no_log(timer->base + CNTV_TVAL);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -900,11 +900,11 @@ void arch_timer_mem_get_cval(u32 *lo, u32 *hi)
|
||||
if (!arch_counter_base)
|
||||
return;
|
||||
|
||||
ctrl = readl_relaxed(arch_counter_base + CNTV_CTL);
|
||||
ctrl = readl_relaxed_no_log(arch_counter_base + CNTV_CTL);
|
||||
|
||||
if (ctrl & ARCH_TIMER_CTRL_ENABLE) {
|
||||
*lo = readl_relaxed(arch_counter_base + CNTCVAL_LO);
|
||||
*hi = readl_relaxed(arch_counter_base + CNTCVAL_HI);
|
||||
*lo = readl_relaxed_no_log(arch_counter_base + CNTCVAL_LO);
|
||||
*hi = readl_relaxed_no_log(arch_counter_base + CNTCVAL_HI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,9 +913,9 @@ static u64 arch_counter_get_cntvct_mem(void)
|
||||
u32 vct_lo, vct_hi, tmp_hi;
|
||||
|
||||
do {
|
||||
vct_hi = readl_relaxed(arch_counter_base + CNTVCT_HI);
|
||||
vct_lo = readl_relaxed(arch_counter_base + CNTVCT_LO);
|
||||
tmp_hi = readl_relaxed(arch_counter_base + CNTVCT_HI);
|
||||
vct_hi = readl_relaxed_no_log(arch_counter_base + CNTVCT_HI);
|
||||
vct_lo = readl_relaxed_no_log(arch_counter_base + CNTVCT_LO);
|
||||
tmp_hi = readl_relaxed_no_log(arch_counter_base + CNTVCT_HI);
|
||||
} while (vct_hi != tmp_hi);
|
||||
|
||||
return ((u64) vct_hi << 32) | vct_lo;
|
||||
@@ -1285,7 +1285,7 @@ arch_timer_mem_find_best_frame(struct arch_timer_mem *timer_mem)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cnttidr = readl_relaxed(cntctlbase + CNTTIDR);
|
||||
cnttidr = readl_relaxed_no_log(cntctlbase + CNTTIDR);
|
||||
|
||||
/*
|
||||
* Try to find a virtual capable frame. Otherwise fall back to a
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/sched/topology.h>
|
||||
#include <linux/sched/sysctl.h>
|
||||
|
||||
#include <trace/events/power.h>
|
||||
|
||||
@@ -659,11 +660,40 @@ static ssize_t show_##file_name \
|
||||
}
|
||||
|
||||
show_one(cpuinfo_min_freq, cpuinfo.min_freq);
|
||||
show_one(cpuinfo_max_freq, cpuinfo.max_freq);
|
||||
show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
|
||||
show_one(scaling_min_freq, min);
|
||||
show_one(scaling_max_freq, max);
|
||||
|
||||
unsigned int cpuinfo_max_freq_cached;
|
||||
|
||||
static bool should_use_cached_freq(int cpu)
|
||||
{
|
||||
/* This is a safe check. may not be needed */
|
||||
if (!cpuinfo_max_freq_cached)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* perfd already configure sched_lib_mask_force to
|
||||
* 0xf0 from user space. so re-using it.
|
||||
*/
|
||||
if (!(BIT(cpu) & sched_lib_mask_force))
|
||||
return false;
|
||||
|
||||
return is_sched_lib_based_app(current->pid);
|
||||
}
|
||||
|
||||
static ssize_t show_cpuinfo_max_freq(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
unsigned int freq = policy->cpuinfo.max_freq;
|
||||
|
||||
if (should_use_cached_freq(policy->cpu))
|
||||
freq = cpuinfo_max_freq_cached << 1;
|
||||
else
|
||||
freq = policy->cpuinfo.max_freq;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", freq);
|
||||
}
|
||||
|
||||
__weak unsigned int arch_freq_get_on_cpu(int cpu)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -62,6 +62,9 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
|
||||
policy->min = policy->cpuinfo.min_freq = min_freq;
|
||||
policy->max = policy->cpuinfo.max_freq = max_freq;
|
||||
|
||||
if (max_freq > cpuinfo_max_freq_cached)
|
||||
cpuinfo_max_freq_cached = max_freq;
|
||||
|
||||
if (policy->min == ~0)
|
||||
return -EINVAL;
|
||||
else
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#elif defined(CONFIG_COMMON_CLK_MSM)
|
||||
#include "../../drivers/clk/msm/clock.h"
|
||||
#endif /* CONFIG_COMMON_CLK */
|
||||
#include "../../kernel/sched/sched.h"
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/trace_msm_low_power.h>
|
||||
|
||||
@@ -724,8 +723,7 @@ static int cpu_power_select(struct cpuidle_device *dev,
|
||||
struct power_params *pwr_params;
|
||||
uint64_t bias_time = 0;
|
||||
|
||||
if ((sleep_disabled && !cpu_isolated(dev->cpu)) ||
|
||||
is_reserved(dev->cpu) || sleep_us < 0)
|
||||
if ((sleep_disabled && !cpu_isolated(dev->cpu)) || sleep_us < 0)
|
||||
return best_level;
|
||||
|
||||
idx_restrict = cpu->nlevels + 1;
|
||||
|
||||
@@ -5912,10 +5912,6 @@ int dsi_display_dev_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
display = platform_get_drvdata(pdev);
|
||||
if (!display || !display->disp_node) {
|
||||
pr_err("invalid display\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* decrement ref count */
|
||||
of_node_put(display->disp_node);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/io.h>
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <soc/qcom/boot_stats.h>
|
||||
@@ -62,7 +63,7 @@ MODULE_PARM_DESC(swfdetect, "Enable soft fault detection");
|
||||
|
||||
#define KGSL_LOG_LEVEL_DEFAULT 3
|
||||
|
||||
static void adreno_pwr_on_work(struct work_struct *work);
|
||||
static void adreno_input_work(struct work_struct *work);
|
||||
static unsigned int counter_delta(struct kgsl_device *device,
|
||||
unsigned int reg, unsigned int *counter);
|
||||
|
||||
@@ -103,6 +104,8 @@ static struct adreno_device device_3d0 = {
|
||||
.ft_policy = KGSL_FT_DEFAULT_POLICY,
|
||||
.ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY,
|
||||
.long_ib_detect = 1,
|
||||
.input_work = __WORK_INITIALIZER(device_3d0.input_work,
|
||||
adreno_input_work),
|
||||
.pwrctrl_flag = BIT(ADRENO_HWCG_CTRL) | BIT(ADRENO_THROTTLING_CTRL),
|
||||
.profile.enabled = false,
|
||||
.active_list = LIST_HEAD_INIT(device_3d0.active_list),
|
||||
@@ -114,8 +117,6 @@ static struct adreno_device device_3d0 = {
|
||||
.skipsaverestore = 1,
|
||||
.usesgmem = 1,
|
||||
},
|
||||
.pwr_on_work = __WORK_INITIALIZER(device_3d0.pwr_on_work,
|
||||
adreno_pwr_on_work),
|
||||
};
|
||||
|
||||
/* Ptr to array for the current set of fault detect registers */
|
||||
@@ -137,6 +138,9 @@ static unsigned int adreno_ft_regs_default[] = {
|
||||
/* Nice level for the higher priority GPU start thread */
|
||||
int adreno_wake_nice = -7;
|
||||
|
||||
/* Number of milliseconds to stay active active after a wake on touch */
|
||||
unsigned int adreno_wake_timeout = 100;
|
||||
|
||||
/**
|
||||
* adreno_readreg64() - Read a 64bit register by getting its offset from the
|
||||
* offset array defined in gpudev node
|
||||
@@ -366,17 +370,152 @@ void adreno_fault_detect_stop(struct adreno_device *adreno_dev)
|
||||
adreno_dev->fast_hang_detect = 0;
|
||||
}
|
||||
|
||||
static void adreno_pwr_on_work(struct work_struct *work)
|
||||
/*
|
||||
* A workqueue callback responsible for actually turning on the GPU after a
|
||||
* touch event. kgsl_pwrctrl_change_state(ACTIVE) is used without any
|
||||
* active_count protection to avoid the need to maintain state. Either
|
||||
* somebody will start using the GPU or the idle timer will fire and put the
|
||||
* GPU back into slumber.
|
||||
*/
|
||||
static void adreno_input_work(struct work_struct *work)
|
||||
{
|
||||
struct adreno_device *adreno_dev =
|
||||
container_of(work, typeof(*adreno_dev), pwr_on_work);
|
||||
struct adreno_device *adreno_dev = container_of(work,
|
||||
struct adreno_device, input_work);
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
|
||||
mutex_lock(&device->mutex);
|
||||
|
||||
device->flags |= KGSL_FLAG_WAKE_ON_TOUCH;
|
||||
|
||||
/*
|
||||
* Don't schedule adreno_start in a high priority workqueue, we are
|
||||
* already in a workqueue which should be sufficient
|
||||
*/
|
||||
kgsl_pwrctrl_change_state(device, KGSL_STATE_ACTIVE);
|
||||
|
||||
/*
|
||||
* When waking up from a touch event we want to stay active long enough
|
||||
* for the user to send a draw command. The default idle timer timeout
|
||||
* is shorter than we want so go ahead and push the idle timer out
|
||||
* further for this special case
|
||||
*/
|
||||
mod_timer(&device->idle_timer,
|
||||
jiffies + msecs_to_jiffies(adreno_wake_timeout));
|
||||
mutex_unlock(&device->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process input events and schedule work if needed. At this point we are only
|
||||
* interested in groking EV_ABS touchscreen events
|
||||
*/
|
||||
static void adreno_input_event(struct input_handle *handle, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
struct kgsl_device *device = handle->handler->private;
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
|
||||
/* Only consider EV_ABS (touch) events */
|
||||
if (type != EV_ABS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Don't do anything if anything hasn't been rendered since we've been
|
||||
* here before
|
||||
*/
|
||||
|
||||
if (device->flags & KGSL_FLAG_WAKE_ON_TOUCH)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the device is in nap, kick the idle timer to make sure that we
|
||||
* don't go into slumber before the first render. If the device is
|
||||
* already in slumber schedule the wake.
|
||||
*/
|
||||
|
||||
if (device->state == KGSL_STATE_NAP) {
|
||||
/*
|
||||
* Set the wake on touch bit to keep from coming back here and
|
||||
* keeping the device in nap without rendering
|
||||
*/
|
||||
|
||||
device->flags |= KGSL_FLAG_WAKE_ON_TOUCH;
|
||||
|
||||
mod_timer(&device->idle_timer,
|
||||
jiffies + device->pwrctrl.interval_timeout);
|
||||
} else if (device->state == KGSL_STATE_SLUMBER) {
|
||||
schedule_work(&adreno_dev->input_work);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INPUT
|
||||
static int adreno_input_connect(struct input_handler *handler,
|
||||
struct input_dev *dev, const struct input_device_id *id)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int ret;
|
||||
|
||||
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
|
||||
if (handle == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
handle->dev = dev;
|
||||
handle->handler = handler;
|
||||
handle->name = handler->name;
|
||||
|
||||
ret = input_register_handle(handle);
|
||||
if (ret) {
|
||||
kfree(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = input_open_device(handle);
|
||||
if (ret) {
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void adreno_input_disconnect(struct input_handle *handle)
|
||||
{
|
||||
input_close_device(handle);
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
#else
|
||||
static int adreno_input_connect(struct input_handler *handler,
|
||||
struct input_dev *dev, const struct input_device_id *id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static void adreno_input_disconnect(struct input_handle *handle) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We are only interested in EV_ABS events so only register handlers for those
|
||||
* input devices that have EV_ABS events
|
||||
*/
|
||||
static const struct input_device_id adreno_input_ids[] = {
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
|
||||
.evbit = { BIT_MASK(EV_ABS) },
|
||||
/* assumption: MT_.._X & MT_.._Y are in the same long */
|
||||
.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
|
||||
BIT_MASK(ABS_MT_POSITION_X) |
|
||||
BIT_MASK(ABS_MT_POSITION_Y) },
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct input_handler adreno_input_handler = {
|
||||
.event = adreno_input_event,
|
||||
.connect = adreno_input_connect,
|
||||
.disconnect = adreno_input_disconnect,
|
||||
.name = "kgsl",
|
||||
.id_table = adreno_input_ids,
|
||||
};
|
||||
|
||||
/*
|
||||
* _soft_reset() - Soft reset GPU
|
||||
* @adreno_dev: Pointer to adreno device
|
||||
@@ -1010,6 +1149,11 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev,
|
||||
&device->pwrctrl.pm_qos_active_latency))
|
||||
device->pwrctrl.pm_qos_active_latency = 501;
|
||||
|
||||
/* get pm-qos-cpu-mask-latency, set it to default if not found */
|
||||
if (of_property_read_u32(node, "qcom,l2pc-cpu-mask-latency",
|
||||
&device->pwrctrl.pm_qos_cpu_mask_latency))
|
||||
device->pwrctrl.pm_qos_cpu_mask_latency = 501;
|
||||
|
||||
/* get pm-qos-wakeup-latency, set it to default if not found */
|
||||
if (of_property_read_u32(node, "qcom,pm-qos-wakeup-latency",
|
||||
&device->pwrctrl.pm_qos_wakeup_latency))
|
||||
@@ -1023,6 +1167,9 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev,
|
||||
device->pwrctrl.bus_control = of_property_read_bool(node,
|
||||
"qcom,bus-control");
|
||||
|
||||
device->pwrctrl.input_disable = of_property_read_bool(node,
|
||||
"qcom,disable-wake-on-touch");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1324,6 +1471,21 @@ static int adreno_probe(struct platform_device *pdev)
|
||||
"Failed to get gpuhtw LLC slice descriptor %ld\n",
|
||||
PTR_ERR(adreno_dev->gpuhtw_llc_slice));
|
||||
|
||||
#ifdef CONFIG_INPUT
|
||||
if (!device->pwrctrl.input_disable) {
|
||||
adreno_input_handler.private = device;
|
||||
/*
|
||||
* It isn't fatal if we cannot register the input handler. Sad,
|
||||
* perhaps, but not fatal
|
||||
*/
|
||||
if (input_register_handler(&adreno_input_handler)) {
|
||||
adreno_input_handler.private = NULL;
|
||||
KGSL_DRV_ERR(device,
|
||||
"Unable to register the input handler\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
place_marker("M - DRIVER GPU Ready");
|
||||
out:
|
||||
if (status) {
|
||||
@@ -1376,6 +1538,10 @@ static int adreno_remove(struct platform_device *pdev)
|
||||
/* The memory is fading */
|
||||
_adreno_free_memories(adreno_dev);
|
||||
|
||||
#ifdef CONFIG_INPUT
|
||||
if (adreno_input_handler.private)
|
||||
input_unregister_handler(&adreno_input_handler);
|
||||
#endif
|
||||
adreno_sysfs_close(adreno_dev);
|
||||
|
||||
adreno_coresight_remove(adreno_dev);
|
||||
@@ -1764,6 +1930,10 @@ static int _adreno_start(struct adreno_device *adreno_dev)
|
||||
/* make sure ADRENO_DEVICE_STARTED is not set here */
|
||||
WARN_ON(test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv));
|
||||
|
||||
/* disallow l2pc during wake up to improve GPU wake up time */
|
||||
kgsl_pwrctrl_update_l2pc(&adreno_dev->dev,
|
||||
KGSL_L2PC_WAKEUP_TIMEOUT);
|
||||
|
||||
pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
|
||||
pmqos_wakeup_vote);
|
||||
|
||||
|
||||
@@ -485,7 +485,7 @@ enum gpu_coresight_sources {
|
||||
* @dispatcher: Container for adreno GPU dispatcher
|
||||
* @pwron_fixup: Command buffer to run a post-power collapse shader workaround
|
||||
* @pwron_fixup_dwords: Number of dwords in the command buffer
|
||||
* @pwr_on_work: Work struct for turning on the GPU
|
||||
* @input_work: Work struct for turning on the GPU after a touch event
|
||||
* @busy_data: Struct holding GPU VBIF busy stats
|
||||
* @ram_cycles_lo: Number of DDR clock cycles for the monitor session (Only
|
||||
* DDR channel 0 read cycles in case of GBIF)
|
||||
@@ -565,7 +565,7 @@ struct adreno_device {
|
||||
struct adreno_dispatcher dispatcher;
|
||||
struct kgsl_memdesc pwron_fixup;
|
||||
unsigned int pwron_fixup_dwords;
|
||||
struct work_struct pwr_on_work;
|
||||
struct work_struct input_work;
|
||||
struct adreno_busy_data busy_data;
|
||||
unsigned int ram_cycles_lo;
|
||||
unsigned int ram_cycles_lo_ch1_read;
|
||||
@@ -1141,6 +1141,7 @@ extern struct adreno_gpudev adreno_a5xx_gpudev;
|
||||
extern struct adreno_gpudev adreno_a6xx_gpudev;
|
||||
|
||||
extern int adreno_wake_nice;
|
||||
extern unsigned int adreno_wake_timeout;
|
||||
|
||||
int adreno_start(struct kgsl_device *device, int priority);
|
||||
int adreno_soft_reset(struct kgsl_device *device);
|
||||
|
||||
@@ -1153,6 +1153,12 @@ static inline int _verify_cmdobj(struct kgsl_device_private *dev_priv,
|
||||
&ADRENO_CONTEXT(context)->base, ib)
|
||||
== false)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Clear the wake on touch bit to indicate an IB has
|
||||
* been submitted since the last time we set it.
|
||||
* But only clear it when we have rendering commands.
|
||||
*/
|
||||
device->flags &= ~KGSL_FLAG_WAKE_ON_TOUCH;
|
||||
}
|
||||
|
||||
/* A3XX does not have support for drawobj profiling */
|
||||
@@ -1447,6 +1453,10 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
|
||||
|
||||
spin_unlock(&drawctxt->lock);
|
||||
|
||||
if (device->pwrctrl.l2pc_update_queue)
|
||||
kgsl_pwrctrl_update_l2pc(&adreno_dev->dev,
|
||||
KGSL_L2PC_QUEUE_TIMEOUT);
|
||||
|
||||
/* Add the context to the dispatcher pending list */
|
||||
dispatcher_queue_context(adreno_dev, drawctxt);
|
||||
|
||||
|
||||
@@ -649,6 +649,7 @@ static ADRENO_SYSFS_BOOL(gpu_llc_slice_enable);
|
||||
static ADRENO_SYSFS_BOOL(gpuhtw_llc_slice_enable);
|
||||
|
||||
static DEVICE_INT_ATTR(wake_nice, 0644, adreno_wake_nice);
|
||||
static DEVICE_INT_ATTR(wake_timeout, 0644, adreno_wake_timeout);
|
||||
|
||||
static ADRENO_SYSFS_BOOL(sptp_pc);
|
||||
static ADRENO_SYSFS_BOOL(lm);
|
||||
@@ -673,6 +674,7 @@ static const struct device_attribute *_attr_list[] = {
|
||||
&adreno_attr_ft_long_ib_detect.attr,
|
||||
&adreno_attr_ft_hang_intr_status.attr,
|
||||
&dev_attr_wake_nice.attr,
|
||||
&dev_attr_wake_timeout.attr,
|
||||
&adreno_attr_sptp_pc.attr,
|
||||
&adreno_attr_lm.attr,
|
||||
&adreno_attr_preemption.attr,
|
||||
|
||||
@@ -5166,6 +5166,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
|
||||
{
|
||||
int status = -EINVAL;
|
||||
struct resource *res;
|
||||
int cpu;
|
||||
|
||||
status = _register_device(device);
|
||||
if (status)
|
||||
@@ -5302,6 +5303,22 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
|
||||
PM_QOS_CPU_DMA_LATENCY,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
if (device->pwrctrl.l2pc_cpus_mask) {
|
||||
struct pm_qos_request *qos = &device->pwrctrl.l2pc_cpus_qos;
|
||||
|
||||
qos->type = PM_QOS_REQ_AFFINE_CORES;
|
||||
|
||||
cpumask_empty(&qos->cpus_affine);
|
||||
for_each_possible_cpu(cpu) {
|
||||
if ((1 << cpu) & device->pwrctrl.l2pc_cpus_mask)
|
||||
cpumask_set_cpu(cpu, &qos->cpus_affine);
|
||||
}
|
||||
|
||||
pm_qos_add_request(&device->pwrctrl.l2pc_cpus_qos,
|
||||
PM_QOS_CPU_DMA_LATENCY,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
device->events_wq = alloc_workqueue("kgsl-events",
|
||||
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
|
||||
|
||||
@@ -5338,6 +5355,8 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
|
||||
kgsl_pwrctrl_uninit_sysfs(device);
|
||||
|
||||
pm_qos_remove_request(&device->pwrctrl.pm_qos_req_dma);
|
||||
if (device->pwrctrl.l2pc_cpus_mask)
|
||||
pm_qos_remove_request(&device->pwrctrl.l2pc_cpus_qos);
|
||||
|
||||
idr_destroy(&device->context_idr);
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ enum kgsl_event_results {
|
||||
KGSL_EVENT_CANCELLED = 2,
|
||||
};
|
||||
|
||||
#define KGSL_FLAG_WAKE_ON_TOUCH BIT(0)
|
||||
#define KGSL_FLAG_SPARSE BIT(1)
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include "kgsl_device.h"
|
||||
#include "kgsl_sync.h"
|
||||
#include "adreno.h"
|
||||
|
||||
static const struct kgsl_ioctl kgsl_ioctl_funcs[] = {
|
||||
KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY,
|
||||
@@ -169,13 +168,8 @@ long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct kgsl_device_private *dev_priv = filep->private_data;
|
||||
struct kgsl_device *device = dev_priv->device;
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
long ret;
|
||||
|
||||
if (cmd == IOCTL_KGSL_GPU_COMMAND &&
|
||||
READ_ONCE(device->state) != KGSL_STATE_ACTIVE)
|
||||
kgsl_schedule_work(&adreno_dev->pwr_on_work);
|
||||
|
||||
ret = kgsl_ioctl_helper(filep, cmd, arg, kgsl_ioctl_funcs,
|
||||
ARRAY_SIZE(kgsl_ioctl_funcs));
|
||||
|
||||
|
||||
@@ -592,6 +592,35 @@ void kgsl_pwrctrl_set_constraint(struct kgsl_device *device,
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_pwrctrl_set_constraint);
|
||||
|
||||
/**
|
||||
* kgsl_pwrctrl_update_l2pc() - Update existing qos request
|
||||
* @device: Pointer to the kgsl_device struct
|
||||
* @timeout_us: the effective duration of qos request in usecs.
|
||||
*
|
||||
* Updates an existing qos request to avoid L2PC on the
|
||||
* CPUs (which are selected through dtsi) on which GPU
|
||||
* thread is running. This would help for performance.
|
||||
*/
|
||||
void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device,
|
||||
unsigned long timeout_us)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (device->pwrctrl.l2pc_cpus_mask == 0)
|
||||
return;
|
||||
|
||||
cpu = get_cpu();
|
||||
put_cpu();
|
||||
|
||||
if ((1 << cpu) & device->pwrctrl.l2pc_cpus_mask) {
|
||||
pm_qos_update_request_timeout(
|
||||
&device->pwrctrl.l2pc_cpus_qos,
|
||||
device->pwrctrl.pm_qos_cpu_mask_latency,
|
||||
timeout_us);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_pwrctrl_update_l2pc);
|
||||
|
||||
static ssize_t kgsl_pwrctrl_thermal_pwrlevel_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
@@ -2322,6 +2351,13 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
|
||||
|
||||
pwr->power_flags = 0;
|
||||
|
||||
kgsl_property_read_u32(device, "qcom,l2pc-cpu-mask",
|
||||
&pwr->l2pc_cpus_mask);
|
||||
|
||||
pwr->l2pc_update_queue = of_property_read_bool(
|
||||
device->pdev->dev.of_node,
|
||||
"qcom,l2pc-update-queue");
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ocmem_bus_node = of_find_node_by_name(
|
||||
@@ -2997,6 +3033,10 @@ _slumber(struct kgsl_device *device)
|
||||
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
|
||||
pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
if (device->pwrctrl.l2pc_cpus_mask)
|
||||
pm_qos_update_request(
|
||||
&device->pwrctrl.l2pc_cpus_qos,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
break;
|
||||
case KGSL_STATE_SUSPEND:
|
||||
complete_all(&device->hwaccess_gate);
|
||||
|
||||
@@ -57,6 +57,19 @@
|
||||
#define KGSL_PWR_DEL_LIMIT 1
|
||||
#define KGSL_PWR_SET_LIMIT 2
|
||||
|
||||
/*
|
||||
* The effective duration of qos request in usecs at queue time.
|
||||
* After timeout, qos request is cancelled automatically.
|
||||
* Kept 80ms default, inline with default GPU idle time.
|
||||
*/
|
||||
#define KGSL_L2PC_QUEUE_TIMEOUT (80 * 1000)
|
||||
|
||||
/*
|
||||
* The effective duration of qos request in usecs at wakeup time.
|
||||
* After timeout, qos request is cancelled automatically.
|
||||
*/
|
||||
#define KGSL_L2PC_WAKEUP_TIMEOUT (10 * 1000)
|
||||
|
||||
enum kgsl_pwrctrl_timer_type {
|
||||
KGSL_PWR_IDLE_TIMER,
|
||||
};
|
||||
@@ -137,9 +150,13 @@ struct kgsl_regulator {
|
||||
* @ahbpath_pcl - CPU to AHB path bus scale identifier
|
||||
* @irq_name - resource name for the IRQ
|
||||
* @clk_stats - structure of clock statistics
|
||||
* @l2pc_cpus_mask - mask to avoid L2PC on masked CPUs
|
||||
* @l2pc_update_queue - Boolean flag to avoid L2PC on masked CPUs at queue time
|
||||
* @l2pc_cpus_qos - qos structure to avoid L2PC on CPUs
|
||||
* @pm_qos_req_dma - the power management quality of service structure
|
||||
* @pm_qos_active_latency - allowed CPU latency in microseconds when active
|
||||
* @pm_qos_cpu_mask_latency - allowed CPU mask latency in microseconds
|
||||
* @input_disable - To disable GPU wakeup on touch input event
|
||||
* @pm_qos_wakeup_latency - allowed CPU latency in microseconds during wakeup
|
||||
* @bus_control - true if the bus calculation is independent
|
||||
* @bus_mod - modifier from the current power level for the bus vote
|
||||
@@ -194,10 +211,14 @@ struct kgsl_pwrctrl {
|
||||
uint32_t ahbpath_pcl;
|
||||
const char *irq_name;
|
||||
struct kgsl_clk_stats clk_stats;
|
||||
unsigned int l2pc_cpus_mask;
|
||||
bool l2pc_update_queue;
|
||||
struct pm_qos_request l2pc_cpus_qos;
|
||||
struct pm_qos_request pm_qos_req_dma;
|
||||
unsigned int pm_qos_active_latency;
|
||||
unsigned int pm_qos_cpu_mask_latency;
|
||||
unsigned int pm_qos_wakeup_latency;
|
||||
bool input_disable;
|
||||
bool bus_control;
|
||||
int bus_mod;
|
||||
unsigned int bus_percent_ab;
|
||||
@@ -265,5 +286,7 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count);
|
||||
void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy);
|
||||
void kgsl_pwrctrl_set_constraint(struct kgsl_device *device,
|
||||
struct kgsl_pwr_constraint *pwrc, uint32_t id);
|
||||
void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device,
|
||||
unsigned long timeout_us);
|
||||
void kgsl_pwrctrl_set_default_gpu_pwrlevel(struct kgsl_device *device);
|
||||
#endif /* __KGSL_PWRCTRL_H */
|
||||
|
||||
@@ -1476,7 +1476,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
|
||||
|
||||
u32 len = hid_report_len(report) + 7;
|
||||
|
||||
return kzalloc(len, flags);
|
||||
return kmalloc(len, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_alloc_report_buf);
|
||||
|
||||
|
||||
@@ -62,30 +62,30 @@ static int stm_ost_send(void __iomem *addr, const void *data, uint32_t size)
|
||||
uint32_t len = size;
|
||||
|
||||
if (((unsigned long)data & 0x1) && (size >= 1)) {
|
||||
writeb_relaxed(*(uint8_t *)data, addr);
|
||||
writeb_relaxed_no_log(*(uint8_t *)data, addr);
|
||||
data++;
|
||||
size--;
|
||||
}
|
||||
if (((unsigned long)data & 0x2) && (size >= 2)) {
|
||||
writew_relaxed(*(uint16_t *)data, addr);
|
||||
writew_relaxed_no_log(*(uint16_t *)data, addr);
|
||||
data += 2;
|
||||
size -= 2;
|
||||
}
|
||||
|
||||
/* now we are 32bit aligned */
|
||||
while (size >= 4) {
|
||||
writel_relaxed(*(uint32_t *)data, addr);
|
||||
writel_relaxed_no_log(*(uint32_t *)data, addr);
|
||||
data += 4;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
if (size >= 2) {
|
||||
writew_relaxed(*(uint16_t *)data, addr);
|
||||
writew_relaxed_no_log(*(uint16_t *)data, addr);
|
||||
data += 2;
|
||||
size -= 2;
|
||||
}
|
||||
if (size >= 1) {
|
||||
writeb_relaxed(*(uint8_t *)data, addr);
|
||||
writeb_relaxed_no_log(*(uint8_t *)data, addr);
|
||||
data++;
|
||||
size--;
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
config CORESIGHT_PLACEHOLDER
|
||||
tristate "Coresight device placeholder driver"
|
||||
default y
|
||||
depends on !CORESIGHT
|
||||
help
|
||||
For targets which do not use coresight, this option enables a placeholder
|
||||
which probes coresight devices to turn down clocks to save power.
|
||||
|
||||
config CORESIGHT_AMBA_PLACEHOLDER
|
||||
tristate "Coresight primecell device placeholder driver"
|
||||
default y
|
||||
depends on !CORESIGHT
|
||||
help
|
||||
For targets which do not use coresight, this option enables a placeholder
|
||||
which probes coresight AMBA devices to turn down clocks to save power.
|
||||
@@ -1,3 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_CORESIGHT_PLACEHOLDER) += coresight-clk-placeholder.o
|
||||
obj-$(CONFIG_CORESIGHT_AMBA_PLACEHOLDER) += coresight-clk-amba-placeholder.o
|
||||
@@ -1,105 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, Google LLC. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
static int coresight_clk_disable_amba_probe(struct amba_device *adev,
|
||||
const struct amba_id *id)
|
||||
{
|
||||
pm_runtime_put(&adev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ETM4x_AMBA_ID(pid) \
|
||||
{ \
|
||||
.id = pid, .mask = 0x000fffff, \
|
||||
}
|
||||
|
||||
#define TMC_ETR_AXI_ARCACHE (0x1U << 1)
|
||||
#define TMC_ETR_SAVE_RESTORE (0x1U << 2)
|
||||
#define CORESIGHT_SOC_600_ETR_CAPS (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE)
|
||||
|
||||
static const struct amba_id coresight_ids[] = {
|
||||
/* ETM4 IDs */
|
||||
ETM4x_AMBA_ID(0x000bb95d), /* Cortex-A53 */
|
||||
ETM4x_AMBA_ID(0x000bb95e), /* Cortex-A57 */
|
||||
ETM4x_AMBA_ID(0x000bb95a), /* Cortex-A72 */
|
||||
ETM4x_AMBA_ID(0x000bb959), /* Cortex-A73 */
|
||||
ETM4x_AMBA_ID(0x000bb9da), /* Cortex-A35 */
|
||||
/* sdmmagpie coresight IDs */
|
||||
ETM4x_AMBA_ID(0x0003b908),
|
||||
ETM4x_AMBA_ID(0x0003b909),
|
||||
ETM4x_AMBA_ID(0x0003b961),
|
||||
ETM4x_AMBA_ID(0x0003b962),
|
||||
ETM4x_AMBA_ID(0x0003b966),
|
||||
ETM4x_AMBA_ID(0x0003b968),
|
||||
ETM4x_AMBA_ID(0x0003b969),
|
||||
ETM4x_AMBA_ID(0x0003b999),
|
||||
ETM4x_AMBA_ID(0x000bb95d),
|
||||
/* dynamic-replicator IDs */
|
||||
{
|
||||
.id = 0x000bb909,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{
|
||||
/* Coresight SoC-600 */
|
||||
.id = 0x000bb9ec,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
/* dynamic-funnel IDs */
|
||||
{
|
||||
.id = 0x000bb908,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{
|
||||
/* Coresight SoC-600 */
|
||||
.id = 0x000bb9eb,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
/* coresight-tmc IDs */
|
||||
{
|
||||
.id = 0x000bb961,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{
|
||||
/* Coresight SoC 600 TMC-ETR/ETS */
|
||||
.id = 0x000bb9e8,
|
||||
.mask = 0x000fffff,
|
||||
.data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS,
|
||||
},
|
||||
{
|
||||
/* Coresight SoC 600 TMC-ETB */
|
||||
.id = 0x000bb9e9,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{
|
||||
/* Coresight SoC 600 TMC-ETF */
|
||||
.id = 0x000bb9ea,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static struct amba_driver coresight_clk_disable_amba_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-clk-disable-amba",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = coresight_clk_disable_amba_probe,
|
||||
.id_table = coresight_ids,
|
||||
};
|
||||
|
||||
module_amba_driver(coresight_clk_disable_amba_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight DEBUGv8 and ETMv4 clock disable AMBA driver stub");
|
||||
MODULE_AUTHOR("J. Avila <elavila@google.com>");
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, Google LLC. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static int coresight_clk_disable_probe(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coresight_clk_disable_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id coresight_clk_disable_match[] = {
|
||||
{ .compatible = "qcom,coresight-csr" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver coresight_clk_disable_driver = {
|
||||
.probe = coresight_clk_disable_probe,
|
||||
.remove = coresight_clk_disable_remove,
|
||||
.driver = {
|
||||
.name = "coresight-clk-disable",
|
||||
.of_match_table = coresight_clk_disable_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(coresight_clk_disable_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight DEBUGv8 and ETMv4 clock disable driver stub");
|
||||
MODULE_AUTHOR("J. Avila <elavila@google.com>");
|
||||
@@ -379,21 +379,11 @@ static int input_get_disposition(struct input_dev *dev,
|
||||
return disposition;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
extern bool ksu_input_hook __read_mostly;
|
||||
extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
#endif
|
||||
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition = input_get_disposition(dev, type, code, &value);
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
if (unlikely(ksu_input_hook))
|
||||
ksu_handle_input_handle_event(&type, &code, &value);
|
||||
#endif
|
||||
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
add_input_randomness(type, code, value);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/msm_rtb.h>
|
||||
#include <linux/wakeup_reason.h>
|
||||
|
||||
#include <linux/irqchip.h>
|
||||
@@ -115,7 +116,7 @@ static void gic_do_wait_for_rwp(void __iomem *base)
|
||||
{
|
||||
u32 count = 1000000; /* 1s! */
|
||||
|
||||
while (readl_relaxed(base + GICD_CTLR) & GICD_CTLR_RWP) {
|
||||
while (readl_relaxed_no_log(base + GICD_CTLR) & GICD_CTLR_RWP) {
|
||||
count--;
|
||||
if (!count) {
|
||||
pr_err_ratelimited("RWP timeout, gone fishing\n");
|
||||
@@ -233,7 +234,8 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
|
||||
else
|
||||
base = gic_data.dist_base;
|
||||
|
||||
return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
|
||||
return !!(readl_relaxed_no_log
|
||||
(base + offset + (gic_irq(d) / 32) * 4) & mask);
|
||||
}
|
||||
|
||||
static void gic_poke_irq(struct irq_data *d, u32 offset)
|
||||
@@ -577,6 +579,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
|
||||
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
|
||||
int err;
|
||||
|
||||
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
|
||||
if (static_key_true(&supports_deactivate))
|
||||
gic_write_eoir(irqnr);
|
||||
else
|
||||
@@ -597,6 +600,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
|
||||
continue;
|
||||
}
|
||||
if (irqnr < 16) {
|
||||
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
|
||||
gic_write_eoir(irqnr);
|
||||
if (static_key_true(&supports_deactivate))
|
||||
gic_write_dir(irqnr);
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/msm_rtb.h>
|
||||
#ifdef CONFIG_PM
|
||||
#include <linux/syscore_ops.h>
|
||||
#endif
|
||||
@@ -505,6 +506,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
|
||||
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
|
||||
isb();
|
||||
handle_domain_irq(gic->domain, irqnr, regs);
|
||||
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
|
||||
continue;
|
||||
}
|
||||
if (irqnr < 16) {
|
||||
@@ -522,6 +524,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
|
||||
smp_rmb();
|
||||
handle_IPI(irqnr, regs);
|
||||
#endif
|
||||
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
menu "KernelSU"
|
||||
|
||||
config KSU
|
||||
tristate "KernelSU function support"
|
||||
default y
|
||||
help
|
||||
Enable kernel-level root privileges on Android System.
|
||||
To compile as a module, choose M here: the
|
||||
module will be called kernelsu.
|
||||
|
||||
config KSU_DEBUG
|
||||
bool "KernelSU debug mode"
|
||||
depends on KSU
|
||||
default n
|
||||
help
|
||||
Enable KernelSU debug mode.
|
||||
|
||||
config KSU_ALLOWLIST_WORKAROUND
|
||||
bool "KernelSU Session init keyring workaround"
|
||||
depends on KSU
|
||||
default n
|
||||
help
|
||||
Enable session keyring init workaround for problematic devices.
|
||||
Useful for situations where the SU allowlist is not kept after a reboot.
|
||||
|
||||
config KSU_CMDLINE
|
||||
bool "Enable KernelSU cmdline"
|
||||
depends on KSU && KSU != m
|
||||
default n
|
||||
help
|
||||
Enable a cmdline called kernelsu.enabled
|
||||
Value 1 means enabled, value 0 means disabled.
|
||||
|
||||
config KSU_MANUAL_HOOK
|
||||
bool "Manual hooking GKI kernels without kprobes"
|
||||
depends on KSU && KSU != m
|
||||
default y if !KPROBES
|
||||
default n
|
||||
help
|
||||
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
||||
If disabled, Hook required KernelSU syscalls with Kernel-probe.
|
||||
|
||||
menu "KernelSU - SUSFS"
|
||||
config KSU_SUSFS
|
||||
bool "KernelSU addon - SUSFS"
|
||||
depends on KSU
|
||||
default y
|
||||
help
|
||||
Patch and Enable SUSFS to kernel with KernelSU.
|
||||
|
||||
config KSU_SUSFS_HAS_MAGIC_MOUNT
|
||||
bool "Say yes if the current KernelSU repo has magic mount implemented (default n)"
|
||||
depends on KSU
|
||||
default y
|
||||
help
|
||||
- Enable to indicate that the current SUSFS kernel supports the auto hide features for 5ec1cff's Magic Mount KernelSU
|
||||
- Every mounts from /debug_ramdisk/workdir will be treated as magic mount and processed differently by susfs
|
||||
|
||||
config KSU_SUSFS_SUS_PATH
|
||||
bool "Enable to hide suspicious path (NOT recommended)"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow hiding the user-defined path and all its sub-paths from various system calls.
|
||||
- tmpfs filesystem is not allowed to be added.
|
||||
- Effective only on zygote spawned user app process.
|
||||
- Use with cautious as it may cause performance loss and will be vulnerable to side channel attacks,
|
||||
just disable this feature if it doesn't work for you or you don't need it at all.
|
||||
|
||||
config KSU_SUSFS_SUS_MOUNT
|
||||
bool "Enable to hide suspicious mounts"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow hiding the user-defined mount paths from /proc/self/[mounts|mountinfo|mountstat].
|
||||
- Effective on all processes for hiding mount entries.
|
||||
- Mounts mounted by process with ksu domain will be forced to be assigned the dev name "KSU".
|
||||
- mnt_id and mnt_group_id of the sus mount will be assigned to a much bigger number to solve the issue of id not being contiguous.
|
||||
|
||||
config KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
|
||||
bool "Enable to hide KSU's default mounts automatically (experimental)"
|
||||
depends on KSU_SUSFS_SUS_MOUNT
|
||||
default y
|
||||
help
|
||||
- Automatically add KSU's default mounts to sus_mount.
|
||||
- No susfs command is needed in userspace.
|
||||
- Only mount operation from process with ksu domain will be checked.
|
||||
|
||||
config KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
|
||||
bool "Enable to hide suspicious bind mounts automatically (experimental)"
|
||||
depends on KSU_SUSFS_SUS_MOUNT
|
||||
default y
|
||||
help
|
||||
- Automatically add binded mounts to sus_mount.
|
||||
- No susfs command is needed in userspace.
|
||||
- Only mount operation from process with ksu domain will be checked.
|
||||
|
||||
config KSU_SUSFS_SUS_KSTAT
|
||||
bool "Enable to spoof suspicious kstat"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow spoofing the kstat of user-defined file/directory.
|
||||
- Effective only on zygote spawned user app process.
|
||||
|
||||
config KSU_SUSFS_SUS_OVERLAYFS
|
||||
bool "Enable to automatically spoof kstat and kstatfs for overlayed files/directories"
|
||||
depends on KSU_SUSFS
|
||||
default n
|
||||
help
|
||||
- Automatically spoof the kstat and kstatfs for overlayed files/directories.
|
||||
- Enable it if you are using legacy KernelSU and dont have auto hide features enabled.
|
||||
- No susfs command is needed in userspace.
|
||||
- Effective on all processes.
|
||||
|
||||
config KSU_SUSFS_TRY_UMOUNT
|
||||
bool "Enable to use ksu's ksu_try_umount"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow using ksu_try_umount to umount other user-defined mount paths prior to ksu's default umount paths.
|
||||
- Effective on all NO-root-access-granted processes.
|
||||
|
||||
config KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
|
||||
bool "Enable to add bind mounts to ksu's ksu_try_umount automatically (experimental)"
|
||||
depends on KSU_SUSFS_TRY_UMOUNT
|
||||
default y
|
||||
help
|
||||
- Automatically add binded mounts to ksu's ksu_try_umount.
|
||||
- No susfs command is needed in userspace.
|
||||
- Only mount operation from process with ksu domain will be checked.
|
||||
|
||||
config KSU_SUSFS_SPOOF_UNAME
|
||||
bool "Enable to spoof uname"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow spoofing the string returned by uname syscall to user-defined string.
|
||||
- Effective on all processes.
|
||||
|
||||
config KSU_SUSFS_ENABLE_LOG
|
||||
bool "Enable logging susfs log to kernel"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow logging susfs log to kernel, uncheck it to completely disable all susfs log.
|
||||
|
||||
config KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS
|
||||
bool "Enable to automatically hide ksu and susfs symbols from /proc/kallsyms"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Automatically hide ksu and susfs symbols from '/proc/kallsyms'.
|
||||
- Effective on all processes.
|
||||
|
||||
config KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG
|
||||
bool "Enable to spoof /proc/bootconfig (gki) or /proc/cmdline (non-gki)"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Spoof the output of /proc/bootconfig (gki) or /proc/cmdline (non-gki) with a user-defined file.
|
||||
- Effective on all processes.
|
||||
|
||||
config KSU_SUSFS_OPEN_REDIRECT
|
||||
bool "Enable to redirect a path to be opened with another path (experimental)"
|
||||
depends on KSU_SUSFS
|
||||
default y
|
||||
help
|
||||
- Allow redirecting a target path to be opened with another user-defined path.
|
||||
- Effective only on processes with uid < 2000.
|
||||
- Please be reminded that process with open access to the target and redirected path can be detected.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,91 +0,0 @@
|
||||
kernelsu-objs := ksu.o
|
||||
kernelsu-objs += allowlist.o
|
||||
kernelsu-objs += apk_sign.o
|
||||
kernelsu-objs += sucompat.o
|
||||
kernelsu-objs += throne_tracker.o
|
||||
kernelsu-objs += core_hook.o
|
||||
kernelsu-objs += ksud.o
|
||||
kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
|
||||
kernelsu-objs += selinux/selinux.o
|
||||
kernelsu-objs += selinux/sepolicy.o
|
||||
kernelsu-objs += selinux/rules.o
|
||||
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||
|
||||
obj-$(CONFIG_KSU) += kernelsu.o
|
||||
|
||||
ccflags-y += -DKSU_VERSION=12998
|
||||
|
||||
# Checks hooks state
|
||||
ifeq ($(strip $(CONFIG_KSU_MANUAL_HOOK)),y)
|
||||
$(info -- KernelSU: CONFIG_KSU_MANUAL_HOOK)
|
||||
else
|
||||
$(info -- KernelSU: CONFIG_KSU_KPROBES_HOOK)
|
||||
ccflags-y += -DCONFIG_KSU_KPROBES_HOOK
|
||||
endif
|
||||
|
||||
# SELinux drivers check
|
||||
ifeq ($(shell grep -q "current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||
endif
|
||||
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||
endif
|
||||
|
||||
# Handle optional backports
|
||||
ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_OPTIONAL_STRNCPY
|
||||
endif
|
||||
ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0)
|
||||
ccflags-y += -DKSU_OPTIONAL_KERNEL_READ
|
||||
endif
|
||||
ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0)
|
||||
ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE
|
||||
endif
|
||||
ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||
ccflags-y += -DKSU_HAS_PATH_UMOUNT
|
||||
endif
|
||||
|
||||
# Checks Samsung UH drivers
|
||||
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
|
||||
ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST
|
||||
endif
|
||||
|
||||
# Samsung SELinux Porting
|
||||
ifeq ($(shell grep -q "SEC_SELINUX_PORTING_COMMON" $(srctree)/security/selinux/avc.c; echo $$?),0)
|
||||
ccflags-y += -DSAMSUNG_SELINUX_PORTING
|
||||
endif
|
||||
|
||||
# Custom Signs
|
||||
ifdef KSU_EXPECTED_SIZE
|
||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||
$(info -- Custom KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||
endif
|
||||
ifdef KSU_EXPECTED_HASH
|
||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||
$(info -- Custom KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||
endif
|
||||
|
||||
ifdef KSU_MANAGER_PACKAGE
|
||||
ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
||||
$(info -- KernelSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||
endif
|
||||
|
||||
$(info -- Supported KernelSU Manager(s): tiann, rsuntk, 5ec1cff)
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||
|
||||
## For susfs stuff ##
|
||||
ifeq ($(shell test -e $(srctree)/fs/susfs.c; echo $$?),0)
|
||||
$(eval SUSFS_VERSION=$(shell cat $(srctree)/include/linux/susfs.h | grep -E '^#define SUSFS_VERSION' | cut -d' ' -f3 | sed 's/"//g'))
|
||||
$(info )
|
||||
$(info -- SUSFS_VERSION: $(SUSFS_VERSION))
|
||||
else
|
||||
$(info -- You have not integrate susfs in your kernel.)
|
||||
$(info -- Read: https://gitlab.com/simonpunk/susfs4ksu)
|
||||
endif
|
||||
|
||||
# Keep a new line here!! Because someone may append config
|
||||
@@ -1,528 +0,0 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#include <linux/compiler_types.h>
|
||||
#endif
|
||||
|
||||
#include "ksu.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "selinux/selinux.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "allowlist.h"
|
||||
#include "manager.h"
|
||||
|
||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||
#define FILE_FORMAT_VERSION 3 // u32
|
||||
|
||||
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
|
||||
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
|
||||
|
||||
static DEFINE_MUTEX(allowlist_mutex);
|
||||
|
||||
// default profiles, these may be used frequently, so we cache it
|
||||
static struct root_profile default_root_profile;
|
||||
static struct non_root_profile default_non_root_profile;
|
||||
|
||||
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
||||
static int allow_list_pointer __read_mostly = 0;
|
||||
|
||||
static void remove_uid_from_arr(uid_t uid)
|
||||
{
|
||||
int *temp_arr;
|
||||
int i, j;
|
||||
|
||||
if (allow_list_pointer == 0)
|
||||
return;
|
||||
|
||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||
if (temp_arr == NULL) {
|
||||
pr_err("%s: unable to allocate memory\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
continue;
|
||||
temp_arr[j++] = allow_list_arr[i];
|
||||
}
|
||||
|
||||
allow_list_pointer = j;
|
||||
|
||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||
temp_arr[j] = -1;
|
||||
|
||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||
kfree(temp_arr);
|
||||
}
|
||||
|
||||
static void init_default_profiles()
|
||||
{
|
||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||
|
||||
default_root_profile.uid = 0;
|
||||
default_root_profile.gid = 0;
|
||||
default_root_profile.groups_count = 1;
|
||||
default_root_profile.groups[0] = 0;
|
||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||
sizeof(default_root_profile.capabilities.effective));
|
||||
default_root_profile.namespaces = 0;
|
||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
|
||||
// This means that we will umount modules by default!
|
||||
default_non_root_profile.umount_modules = true;
|
||||
}
|
||||
|
||||
struct perm_data {
|
||||
struct list_head list;
|
||||
struct app_profile profile;
|
||||
};
|
||||
|
||||
static struct list_head allow_list;
|
||||
|
||||
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
|
||||
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
|
||||
static bool persistent_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
pr_info("ksu_show_allow_list\n");
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||
p->profile.allow_su);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
static void ksu_grant_root_to_shell()
|
||||
{
|
||||
struct app_profile profile = {
|
||||
.version = KSU_APP_PROFILE_VER,
|
||||
.allow_su = true,
|
||||
.current_uid = 2000,
|
||||
};
|
||||
strcpy(profile.key, "com.android.shell");
|
||||
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
ksu_set_app_profile(&profile, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ksu_get_app_profile(struct app_profile *profile)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
bool found = false;
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||
if (uid_match) {
|
||||
// found it, override it with ours
|
||||
memcpy(profile, &p->profile, sizeof(*profile));
|
||||
found = true;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return found;
|
||||
}
|
||||
|
||||
static inline bool forbid_system_uid(uid_t uid) {
|
||||
#define SHELL_UID 2000
|
||||
#define SYSTEM_UID 1000
|
||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||
}
|
||||
|
||||
static bool profile_valid(struct app_profile *profile)
|
||||
{
|
||||
if (!profile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->allow_su) {
|
||||
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
bool result = false;
|
||||
|
||||
if (!profile_valid(profile)) {
|
||||
pr_err("Failed to set app profile: invalid profile!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||
if (profile->current_uid == p->profile.current_uid &&
|
||||
!strcmp(profile->key, p->profile.key)) {
|
||||
// found it, just override it all!
|
||||
memcpy(&p->profile, profile, sizeof(*profile));
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// not found, alloc a new node!
|
||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||
if (!p) {
|
||||
pr_err("ksu_set_app_profile alloc failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&p->profile, profile, sizeof(*profile));
|
||||
if (profile->allow_su) {
|
||||
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||
profile->key, profile->current_uid,
|
||||
profile->rp_config.profile.gid,
|
||||
profile->rp_config.profile.selinux_domain);
|
||||
} else {
|
||||
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||
profile->key, profile->current_uid,
|
||||
profile->nrp_config.profile.umount_modules);
|
||||
}
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
|
||||
out:
|
||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||
if (profile->allow_su)
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
||||
else
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||
} else {
|
||||
if (profile->allow_su) {
|
||||
/*
|
||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||
* registered to request superuser?
|
||||
*/
|
||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||
pr_err("too many apps registered\n");
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||
} else {
|
||||
remove_uid_from_arr(profile->current_uid);
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
|
||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||
// set default non root profile
|
||||
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||
sizeof(default_non_root_profile));
|
||||
}
|
||||
|
||||
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||
// set default root profile
|
||||
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||
sizeof(default_root_profile));
|
||||
}
|
||||
|
||||
if (persist)
|
||||
persistent_allow_list();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __ksu_is_allow_uid(uid_t uid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (unlikely(uid == 0)) {
|
||||
// already root, but only allow our domain.
|
||||
return ksu_is_ksu_domain();
|
||||
}
|
||||
|
||||
if (forbid_system_uid(uid)) {
|
||||
// do not bother going through the list if it's system
|
||||
return false;
|
||||
}
|
||||
|
||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||
// manager is always allowed!
|
||||
return true;
|
||||
}
|
||||
|
||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||
} else {
|
||||
for (i = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ksu_uid_should_umount(uid_t uid)
|
||||
{
|
||||
struct app_profile profile = { .current_uid = uid };
|
||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||
// we should not umount on manager!
|
||||
return false;
|
||||
}
|
||||
bool found = ksu_get_app_profile(&profile);
|
||||
if (!found) {
|
||||
// no app profile found, it must be non root app
|
||||
return default_non_root_profile.umount_modules;
|
||||
}
|
||||
if (profile.allow_su) {
|
||||
// if found and it is granted to su, we shouldn't umount for it
|
||||
return false;
|
||||
} else {
|
||||
// found an app profile
|
||||
if (profile.nrp_config.use_default) {
|
||||
return default_non_root_profile.umount_modules;
|
||||
} else {
|
||||
return profile.nrp_config.profile.umount_modules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||
if (!p->profile.rp_config.use_default) {
|
||||
return &p->profile.rp_config.profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use default profile
|
||||
return &default_root_profile;
|
||||
}
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
int i = 0;
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
if (p->profile.allow_su == allow) {
|
||||
array[i++] = p->profile.current_uid;
|
||||
}
|
||||
}
|
||||
*length = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void do_save_allow_list(struct work_struct *work)
|
||||
{
|
||||
u32 magic = FILE_MAGIC;
|
||||
u32 version = FILE_FORMAT_VERSION;
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
loff_t off = 0;
|
||||
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
// store magic and version
|
||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
||||
sizeof(magic)) {
|
||||
pr_err("save_allow_list write magic failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
||||
sizeof(version)) {
|
||||
pr_err("save_allow_list write version failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("save allow list, name: %s uid: %d, allow: %d\n",
|
||||
p->profile.key, p->profile.current_uid,
|
||||
p->profile.allow_su);
|
||||
|
||||
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
||||
&off);
|
||||
}
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static void do_load_allow_list(struct work_struct *work)
|
||||
{
|
||||
loff_t off = 0;
|
||||
ssize_t ret = 0;
|
||||
struct file *fp = NULL;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
// always allow adb shell by default
|
||||
ksu_grant_root_to_shell();
|
||||
#endif
|
||||
|
||||
// load allowlist now!
|
||||
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
// verify magic
|
||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
||||
sizeof(magic) ||
|
||||
magic != FILE_MAGIC) {
|
||||
pr_err("allowlist file invalid: %d!\n", magic);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
||||
sizeof(version)) {
|
||||
pr_err("allowlist read version: %d failed\n", version);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pr_info("allowlist version: %d\n", version);
|
||||
|
||||
while (true) {
|
||||
struct app_profile profile;
|
||||
|
||||
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
||||
&off);
|
||||
|
||||
if (ret <= 0) {
|
||||
pr_info("load_allow_list read err: %zd\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
||||
profile.key, profile.current_uid, profile.allow_su);
|
||||
ksu_set_app_profile(&profile, false);
|
||||
}
|
||||
|
||||
exit:
|
||||
ksu_show_allow_list();
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
||||
{
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
bool modified = false;
|
||||
// TODO: use RCU!
|
||||
mutex_lock(&allowlist_mutex);
|
||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||
uid_t uid = np->profile.current_uid;
|
||||
char *package = np->profile.key;
|
||||
// we use this uid for special cases, don't prune it!
|
||||
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
||||
modified = true;
|
||||
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||
list_del(&np->list);
|
||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
||||
}
|
||||
remove_uid_from_arr(uid);
|
||||
smp_mb();
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
|
||||
if (modified) {
|
||||
persistent_allow_list();
|
||||
}
|
||||
}
|
||||
|
||||
// make sure allow list works cross boot
|
||||
static bool persistent_allow_list(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_save_work);
|
||||
}
|
||||
|
||||
bool ksu_load_allow_list(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_load_work);
|
||||
}
|
||||
|
||||
void ksu_allowlist_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||
allow_list_arr[i] = -1;
|
||||
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||
|
||||
init_default_profiles();
|
||||
}
|
||||
|
||||
void ksu_allowlist_exit(void)
|
||||
{
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
do_save_allow_list(NULL);
|
||||
|
||||
// free allowlist
|
||||
mutex_lock(&allowlist_mutex);
|
||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef __KSU_H_ALLOWLIST
|
||||
#define __KSU_H_ALLOWLIST
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "ksu.h"
|
||||
|
||||
void ksu_allowlist_init(void);
|
||||
|
||||
void ksu_allowlist_exit(void);
|
||||
|
||||
bool ksu_load_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void);
|
||||
|
||||
bool __ksu_is_allow_uid(uid_t uid);
|
||||
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||
|
||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
||||
|
||||
bool ksu_get_app_profile(struct app_profile *);
|
||||
bool ksu_set_app_profile(struct app_profile *, bool persist);
|
||||
|
||||
bool ksu_uid_should_umount(uid_t uid);
|
||||
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||
#endif
|
||||
@@ -1,335 +0,0 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
#include <linux/moduleparam.h>
|
||||
#endif
|
||||
#include <crypto/hash.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||
#include <crypto/sha2.h>
|
||||
#else
|
||||
#include <crypto/sha.h>
|
||||
#endif
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
#include "manager_sign.h"
|
||||
|
||||
struct sdesc {
|
||||
struct shash_desc shash;
|
||||
char ctx[];
|
||||
};
|
||||
|
||||
static struct apk_sign_key {
|
||||
unsigned size;
|
||||
const char *sha256;
|
||||
} apk_sign_keys[] = {
|
||||
{EXPECTED_SIZE_OFFICIAL, EXPECTED_HASH_OFFICIAL}, // Official
|
||||
{EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // RKSU
|
||||
{EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF}, // MKSU
|
||||
{EXPECTED_SIZE_NEXT, EXPECTED_HASH_NEXT}, // ksu-next
|
||||
#ifdef EXPECTED_SIZE
|
||||
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||
{
|
||||
struct sdesc *sdesc;
|
||||
int size;
|
||||
|
||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||
sdesc = kmalloc(size, GFP_KERNEL);
|
||||
if (!sdesc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
sdesc->shash.tfm = alg;
|
||||
return sdesc;
|
||||
}
|
||||
|
||||
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
||||
unsigned int datalen, unsigned char *digest)
|
||||
{
|
||||
struct sdesc *sdesc;
|
||||
int ret;
|
||||
|
||||
sdesc = init_sdesc(alg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("can't alloc sdesc\n");
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
|
||||
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
||||
kfree(sdesc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||
unsigned char *digest)
|
||||
{
|
||||
struct crypto_shash *alg;
|
||||
char *hash_alg_name = "sha256";
|
||||
int ret;
|
||||
|
||||
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
||||
if (IS_ERR(alg)) {
|
||||
pr_info("can't alloc alg %s\n", hash_alg_name);
|
||||
return PTR_ERR(alg);
|
||||
}
|
||||
ret = calc_hash(alg, data, datalen, digest);
|
||||
crypto_free_shash(alg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset)
|
||||
{
|
||||
int i;
|
||||
struct apk_sign_key sign_key;
|
||||
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
||||
|
||||
*offset += 0x4 * 3;
|
||||
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
||||
|
||||
*pos += *size4;
|
||||
*offset += 0x4 + *size4;
|
||||
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||
*offset += 0x4 * 2;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||
sign_key = apk_sign_keys[i];
|
||||
|
||||
if (*size4 != sign_key.size)
|
||||
continue;
|
||||
*offset += *size4;
|
||||
|
||||
#define CERT_MAX_LENGTH 1024
|
||||
char cert[CERT_MAX_LENGTH];
|
||||
if (*size4 > CERT_MAX_LENGTH) {
|
||||
pr_info("cert length overlimit\n");
|
||||
return false;
|
||||
}
|
||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
||||
pr_info("sha256 error\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||
|
||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||
pr_info("sha256: %s, expected: %s\n", hash_str,
|
||||
sign_key.sha256);
|
||||
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct zip_entry_header {
|
||||
uint32_t signature;
|
||||
uint16_t version;
|
||||
uint16_t flags;
|
||||
uint16_t compression;
|
||||
uint16_t mod_time;
|
||||
uint16_t mod_date;
|
||||
uint32_t crc32;
|
||||
uint32_t compressed_size;
|
||||
uint32_t uncompressed_size;
|
||||
uint16_t file_name_length;
|
||||
uint16_t extra_field_length;
|
||||
} __attribute__((packed));
|
||||
|
||||
// This is a necessary but not sufficient condition, but it is enough for us
|
||||
static bool has_v1_signature_file(struct file *fp)
|
||||
{
|
||||
struct zip_entry_header header;
|
||||
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
||||
|
||||
loff_t pos = 0;
|
||||
|
||||
while (ksu_kernel_read_compat(fp, &header,
|
||||
sizeof(struct zip_entry_header), &pos) ==
|
||||
sizeof(struct zip_entry_header)) {
|
||||
if (header.signature != 0x04034b50) {
|
||||
// ZIP magic: 'PK'
|
||||
return false;
|
||||
}
|
||||
// Read the entry file name
|
||||
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
||||
char fileName[sizeof(MANIFEST)];
|
||||
ksu_kernel_read_compat(fp, fileName,
|
||||
header.file_name_length, &pos);
|
||||
fileName[header.file_name_length] = '\0';
|
||||
|
||||
// Check if the entry matches META-INF/MANIFEST.MF
|
||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||
0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Skip the entry file name
|
||||
pos += header.file_name_length;
|
||||
}
|
||||
|
||||
// Skip to the next entry
|
||||
pos += header.extra_field_length + header.compressed_size;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool check_v2_signature(char *path)
|
||||
{
|
||||
unsigned char buffer[0x11] = { 0 };
|
||||
u32 size4;
|
||||
u64 size8, size_of_block;
|
||||
|
||||
loff_t pos;
|
||||
|
||||
bool v2_signing_valid = false;
|
||||
int v2_signing_blocks = 0;
|
||||
bool v3_signing_exist = false;
|
||||
bool v3_1_signing_exist = false;
|
||||
|
||||
int i;
|
||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("open %s error.\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable inotify for this file
|
||||
fp->f_mode |= FMODE_NONOTIFY;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||
for (i = 0;; ++i) {
|
||||
unsigned short n;
|
||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
||||
if (n == i) {
|
||||
pos -= 22;
|
||||
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0xffff) {
|
||||
pr_info("error: cannot find eocd\n");
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
pos += 12;
|
||||
// offset
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
||||
pos = size4 - 0x18;
|
||||
|
||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
||||
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
||||
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
pos = size4 - (size8 + 0x8);
|
||||
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
||||
if (size_of_block != size8) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
int loop_count = 0;
|
||||
while (loop_count++ < 10) {
|
||||
uint32_t id;
|
||||
uint32_t offset;
|
||||
ksu_kernel_read_compat(fp, &size8, 0x8,
|
||||
&pos); // sequence length
|
||||
if (size8 == size_of_block) {
|
||||
break;
|
||||
}
|
||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
||||
offset = 4;
|
||||
if (id == 0x7109871au) {
|
||||
v2_signing_blocks++;
|
||||
v2_signing_valid = check_block(fp, &size4, &pos, &offset);
|
||||
} else if (id == 0xf05368c0u) {
|
||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||
v3_signing_exist = true;
|
||||
} else if (id == 0x1b93ad61u) {
|
||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||
v3_1_signing_exist = true;
|
||||
} else {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("Unknown id: 0x%08x\n", id);
|
||||
#endif
|
||||
}
|
||||
pos += (size8 - offset);
|
||||
}
|
||||
|
||||
if (v2_signing_blocks != 1) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_err("Unexpected v2 signature count: %d\n",
|
||||
v2_signing_blocks);
|
||||
#endif
|
||||
v2_signing_valid = false;
|
||||
}
|
||||
|
||||
if (v2_signing_valid) {
|
||||
int has_v1_signing = has_v1_signature_file(fp);
|
||||
if (has_v1_signing) {
|
||||
pr_err("Unexpected v1 signature scheme found!\n");
|
||||
filp_close(fp, 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
clean:
|
||||
filp_close(fp, 0);
|
||||
|
||||
if (v3_signing_exist || v3_1_signing_exist) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_err("Unexpected v3 signature scheme found!\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return v2_signing_valid;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
|
||||
int ksu_debug_manager_uid = -1;
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int rv = param_set_uint(val, kp);
|
||||
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static struct kernel_param_ops expected_size_ops = {
|
||||
.set = set_expected_size,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
|
||||
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||
|
||||
#endif
|
||||
|
||||
bool ksu_is_manager_apk(char *path)
|
||||
{
|
||||
return check_v2_signature(path);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef __KSU_H_APK_V2_SIGN
|
||||
#define __KSU_H_APK_V2_SIGN
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
bool ksu_is_manager_apk(char *path);
|
||||
|
||||
#endif
|
||||
@@ -1,102 +0,0 @@
|
||||
#ifndef __KSU_H_ARCH
|
||||
#define __KSU_H_ARCH
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#define __PT_PARM1_REG regs[0]
|
||||
#define __PT_PARM2_REG regs[1]
|
||||
#define __PT_PARM3_REG regs[2]
|
||||
#define __PT_SYSCALL_PARM4_REG regs[3]
|
||||
#define __PT_CCALL_PARM4_REG regs[3]
|
||||
#define __PT_PARM5_REG regs[4]
|
||||
#define __PT_PARM6_REG regs[5]
|
||||
#define __PT_RET_REG regs[30]
|
||||
#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG regs[0]
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG pc
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
||||
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
|
||||
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
|
||||
#else
|
||||
#define PRCTL_SYMBOL "sys_prctl"
|
||||
#define SYS_READ_SYMBOL "sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||
#endif
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
#define __PT_PARM1_REG di
|
||||
#define __PT_PARM2_REG si
|
||||
#define __PT_PARM3_REG dx
|
||||
/* syscall uses r10 for PARM4 */
|
||||
#define __PT_SYSCALL_PARM4_REG r10
|
||||
#define __PT_CCALL_PARM4_REG cx
|
||||
#define __PT_PARM5_REG r8
|
||||
#define __PT_PARM6_REG r9
|
||||
#define __PT_RET_REG sp
|
||||
#define __PT_FP_REG bp
|
||||
#define __PT_RC_REG ax
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG ip
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
||||
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
|
||||
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
|
||||
#else
|
||||
#define PRCTL_SYMBOL "sys_prctl"
|
||||
#define SYS_READ_SYMBOL "sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
#error "Unsupported arch"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* allow some architecutres to override `struct pt_regs` */
|
||||
#ifndef __PT_REGS_CAST
|
||||
#define __PT_REGS_CAST(x) (x)
|
||||
#endif
|
||||
|
||||
#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG)
|
||||
#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
|
||||
#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
|
||||
#define PT_REGS_SYSCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_SYSCALL_PARM4_REG)
|
||||
#define PT_REGS_CCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_CCALL_PARM4_REG)
|
||||
#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
|
||||
#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG)
|
||||
#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
|
||||
#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG)
|
||||
#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG)
|
||||
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
|
||||
#else
|
||||
#define PT_REAL_REGS(regs) ((regs))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
#ifndef __KSU_H_KSU_CORE
|
||||
#define __KSU_H_KSU_CORE
|
||||
|
||||
#include <linux/init.h>
|
||||
|
||||
void __init ksu_core_init(void);
|
||||
void ksu_core_exit(void);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +0,0 @@
|
||||
// WARNING: THIS IS A STUB FILE
|
||||
// This file will be regenerated by CI
|
||||
|
||||
unsigned int ksud_size = 0;
|
||||
const char ksud[0] = {};
|
||||
@@ -1,2 +0,0 @@
|
||||
register_kprobe
|
||||
unregister_kprobe
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef __KSU_H_KSHOOK
|
||||
#define __KSU_H_KSHOOK
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
// For sucompat
|
||||
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags);
|
||||
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
|
||||
// For ksud
|
||||
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos);
|
||||
|
||||
// For ksud and sucompat
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags);
|
||||
|
||||
// For volume button
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value);
|
||||
|
||||
#endif
|
||||
@@ -1,194 +0,0 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#include <linux/sched/task.h>
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
#include <linux/uaccess.h>
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h" // Add check Huawei Device
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||
defined(CONFIG_IS_HW_HISI) || \
|
||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
#include <linux/key.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/cred.h>
|
||||
struct key *init_session_keyring = NULL;
|
||||
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern struct task_struct init_task;
|
||||
|
||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
||||
struct ksu_ns_fs_saved {
|
||||
struct nsproxy *ns;
|
||||
struct fs_struct *fs;
|
||||
};
|
||||
|
||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
ns_fs_saved->ns = current->nsproxy;
|
||||
ns_fs_saved->fs = current->fs;
|
||||
}
|
||||
|
||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
current->nsproxy = ns_fs_saved->ns;
|
||||
current->fs = ns_fs_saved->fs;
|
||||
}
|
||||
|
||||
static bool android_context_saved_checked = false;
|
||||
static bool android_context_saved_enabled = false;
|
||||
static struct ksu_ns_fs_saved android_context_saved;
|
||||
|
||||
void ksu_android_ns_fs_check()
|
||||
{
|
||||
if (android_context_saved_checked)
|
||||
return;
|
||||
android_context_saved_checked = true;
|
||||
task_lock(current);
|
||||
if (current->nsproxy && current->fs &&
|
||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||
android_context_saved_enabled = true;
|
||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||
ksu_save_ns_fs(&android_context_saved);
|
||||
} else {
|
||||
pr_info("android context saved disabled\n");
|
||||
}
|
||||
task_unlock(current);
|
||||
}
|
||||
|
||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||
defined(CONFIG_IS_HW_HISI) || \
|
||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||
(current->flags & PF_WQ_WORKER)) {
|
||||
pr_info("installing init session keyring for older kernel\n");
|
||||
install_session_keyring(init_session_keyring);
|
||||
}
|
||||
#endif
|
||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||
struct ksu_ns_fs_saved saved;
|
||||
if (android_context_saved_enabled) {
|
||||
pr_info("start switch current nsproxy and fs to android context\n");
|
||||
task_lock(current);
|
||||
ksu_save_ns_fs(&saved);
|
||||
ksu_load_ns_fs(&android_context_saved);
|
||||
task_unlock(current);
|
||||
}
|
||||
struct file *fp = filp_open(filename, flags, mode);
|
||||
if (android_context_saved_enabled) {
|
||||
task_lock(current);
|
||||
ksu_load_ns_fs(&saved);
|
||||
task_unlock(current);
|
||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_READ)
|
||||
return kernel_read(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||
if (pos && result > 0) {
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_WRITE)
|
||||
return kernel_write(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_write(p, buf, count, offset);
|
||||
if (pos && result > 0) {
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || defined(KSU_OPTIONAL_STRNCPY)
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||
}
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
||||
}
|
||||
#else
|
||||
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
long ret;
|
||||
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
|
||||
set_fs(USER_DS);
|
||||
pagefault_disable();
|
||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||
pagefault_enable();
|
||||
set_fs(old_fs);
|
||||
|
||||
if (ret >= count) {
|
||||
ret = count;
|
||||
dst[ret - 1] = '\0';
|
||||
} else if (ret > 0) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
long ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||
if (likely(ret >= 0))
|
||||
return ret;
|
||||
|
||||
// we faulted! fallback to slow path
|
||||
if (unlikely(!ksu_access_ok(unsafe_addr, count)))
|
||||
return -EFAULT;
|
||||
|
||||
return strncpy_from_user(dst, unsafe_addr, count);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef __KSU_H_KERNEL_COMPAT
|
||||
#define __KSU_H_KERNEL_COMPAT
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/cred.h>
|
||||
#include "ss/policydb.h"
|
||||
#include "linux/key.h"
|
||||
|
||||
/*
|
||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||
* From ss/ebitmap.h
|
||||
*/
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||
#define CONFIG_IS_HW_HISI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Checks for UH, KDP and RKP
|
||||
#ifdef SAMSUNG_UH_DRIVER_EXIST
|
||||
#if defined(CONFIG_UH) || defined(CONFIG_KDP) || defined(CONFIG_RKP)
|
||||
#error "CONFIG_UH, CONFIG_KDP and CONFIG_RKP is enabled! Please disable or remove it before compile a kernel with KernelSU!"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||
const void __user *unsafe_addr,
|
||||
long count);
|
||||
extern long ksu_strncpy_from_user_retry(char *dst,
|
||||
const void __user *unsafe_addr,
|
||||
long count);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||
defined(CONFIG_IS_HW_HISI) || \
|
||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
extern struct key *init_session_keyring;
|
||||
#endif
|
||||
|
||||
extern void ksu_android_ns_fs_check();
|
||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||
umode_t mode);
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
loff_t *pos);
|
||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||
size_t count, loff_t *pos);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
||||
#else
|
||||
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef __KSU_H_KLOG
|
||||
#define __KSU_H_KLOG
|
||||
|
||||
#include <linux/printk.h>
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "KernelSU: " fmt
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,143 +0,0 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "core_hook.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "throne_tracker.h"
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
#include <linux/susfs.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_CMDLINE
|
||||
#include <linux/init.h>
|
||||
|
||||
// use get_ksu_state()!
|
||||
unsigned int enable_kernelsu = 1; // enabled by default
|
||||
static int __init read_kernelsu_state(char *s)
|
||||
{
|
||||
if (s)
|
||||
enable_kernelsu = simple_strtoul(s, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
__setup("kernelsu.enabled=", read_kernelsu_state);
|
||||
|
||||
bool get_ksu_state(void) { return enable_kernelsu >= 1; }
|
||||
#else
|
||||
bool get_ksu_state(void) { return true; }
|
||||
#endif /* CONFIG_KSU_CMDLINE */
|
||||
|
||||
static struct workqueue_struct *ksu_workqueue;
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work)
|
||||
{
|
||||
return queue_work(ksu_workqueue, work);
|
||||
}
|
||||
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags)
|
||||
{
|
||||
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||
flags);
|
||||
}
|
||||
|
||||
extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
extern void ksu_ksud_init();
|
||||
extern void ksu_ksud_exit();
|
||||
|
||||
int __init ksu_kernelsu_init(void)
|
||||
{
|
||||
pr_info("kernelsu.enabled=%d\n",
|
||||
(int)get_ksu_state());
|
||||
|
||||
#ifdef CONFIG_KSU_CMDLINE
|
||||
if (!get_ksu_state()) {
|
||||
pr_info_once("drivers is disabled.");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_alert("*************************************************************");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("*************************************************************");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
susfs_init();
|
||||
#endif
|
||||
|
||||
ksu_core_init();
|
||||
|
||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||
|
||||
ksu_allowlist_init();
|
||||
|
||||
ksu_throne_tracker_init();
|
||||
|
||||
ksu_sucompat_init();
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_ksud_init();
|
||||
#else
|
||||
pr_debug("init ksu driver\n");
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
#ifndef CONFIG_KSU_DEBUG
|
||||
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksu_kernelsu_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KSU_CMDLINE
|
||||
if (!get_ksu_state()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ksu_allowlist_exit();
|
||||
|
||||
ksu_throne_tracker_exit();
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_ksud_exit();
|
||||
#endif
|
||||
ksu_sucompat_exit();
|
||||
|
||||
ksu_core_exit();
|
||||
}
|
||||
|
||||
module_init(ksu_kernelsu_init);
|
||||
module_exit(ksu_kernelsu_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("weishu");
|
||||
MODULE_DESCRIPTION("Android KernelSU");
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||
#endif
|
||||
@@ -1,104 +0,0 @@
|
||||
#ifndef __KSU_H_KSU
|
||||
#define __KSU_H_KSU
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define KERNEL_SU_VERSION KSU_VERSION
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_REPORT_EVENT 7
|
||||
#define CMD_SET_SEPOLICY 8
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
#define CMD_GET_APP_PROFILE 10
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
#define CMD_GET_MANAGER_UID 16
|
||||
|
||||
#define CMD_HOOK_MODE 0xC0DEAD1A
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
#define EVENT_MODULE_MOUNTED 3
|
||||
|
||||
#define KSU_APP_PROFILE_VER 2
|
||||
#define KSU_MAX_PACKAGE_NAME 256
|
||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||
#define KSU_MAX_GROUPS 32
|
||||
#define KSU_SELINUX_DOMAIN 64
|
||||
|
||||
struct root_profile {
|
||||
int32_t uid;
|
||||
int32_t gid;
|
||||
|
||||
int32_t groups_count;
|
||||
int32_t groups[KSU_MAX_GROUPS];
|
||||
|
||||
// kernel_cap_t is u32[2] for capabilities v3
|
||||
struct {
|
||||
u64 effective;
|
||||
u64 permitted;
|
||||
u64 inheritable;
|
||||
} capabilities;
|
||||
|
||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||
|
||||
int32_t namespaces;
|
||||
};
|
||||
|
||||
struct non_root_profile {
|
||||
bool umount_modules;
|
||||
};
|
||||
|
||||
struct app_profile {
|
||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||
u32 version;
|
||||
|
||||
// this is usually the package of the app, but can be other value for special apps
|
||||
char key[KSU_MAX_PACKAGE_NAME];
|
||||
int32_t current_uid;
|
||||
bool allow_su;
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool use_default;
|
||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||
|
||||
struct root_profile profile;
|
||||
} rp_config;
|
||||
|
||||
struct {
|
||||
bool use_default;
|
||||
|
||||
struct non_root_profile profile;
|
||||
} nrp_config;
|
||||
};
|
||||
};
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work);
|
||||
|
||||
static inline int startswith(char *s, char *prefix)
|
||||
{
|
||||
return strncmp(s, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
static inline int endswith(const char *s, const char *t)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
size_t tlen = strlen(t);
|
||||
if (tlen > slen)
|
||||
return 1;
|
||||
return strcmp(s + slen - tlen, t);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,683 +0,0 @@
|
||||
#include <asm/current.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
#include <linux/input-event-codes.h>
|
||||
#else
|
||||
#include <uapi/linux/input.h>
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
||||
#include <linux/aio.h>
|
||||
#endif
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "selinux/selinux.h"
|
||||
|
||||
bool ksu_is_compat __read_mostly = false; // let it here
|
||||
|
||||
static const char KERNEL_SU_RC[] =
|
||||
"\n"
|
||||
|
||||
"on post-fs-data\n"
|
||||
" start logd\n"
|
||||
// We should wait for the post-fs-data finish
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
|
||||
"\n"
|
||||
|
||||
"on nonencrypted\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||
"\n"
|
||||
|
||||
"on property:vold.decrypt=trigger_restart_framework\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||
"\n"
|
||||
|
||||
"on property:sys.boot_completed=1\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
|
||||
"\n"
|
||||
|
||||
"\n";
|
||||
|
||||
static void stop_vfs_read_hook();
|
||||
static void stop_execve_hook();
|
||||
static void stop_input_hook();
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static struct work_struct stop_vfs_read_work;
|
||||
static struct work_struct stop_execve_hook_work;
|
||||
static struct work_struct stop_input_hook_work;
|
||||
#else
|
||||
bool ksu_vfs_read_hook __read_mostly = true;
|
||||
bool ksu_execveat_hook __read_mostly = true;
|
||||
bool ksu_input_hook __read_mostly = true;
|
||||
#endif
|
||||
|
||||
u32 ksu_devpts_sid;
|
||||
|
||||
void ksu_on_post_fs_data(void)
|
||||
{
|
||||
static bool done = false;
|
||||
if (done) {
|
||||
pr_info("%s already done\n", __func__);
|
||||
return;
|
||||
}
|
||||
done = true;
|
||||
pr_info("%s!\n", __func__);
|
||||
ksu_load_allow_list();
|
||||
// sanity check, this may influence the performance
|
||||
stop_input_hook();
|
||||
|
||||
ksu_devpts_sid = ksu_get_devpts_sid();
|
||||
pr_info("devpts sid: %d\n", ksu_devpts_sid);
|
||||
}
|
||||
|
||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||
struct user_arg_ptr {
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool is_compat;
|
||||
#endif
|
||||
union {
|
||||
const char __user *const __user *native;
|
||||
#ifdef CONFIG_COMPAT
|
||||
const compat_uptr_t __user *compat;
|
||||
#endif
|
||||
} ptr;
|
||||
};
|
||||
|
||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
{
|
||||
const char __user *native;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (unlikely(argv.is_compat)) {
|
||||
compat_uptr_t compat;
|
||||
|
||||
if (get_user(compat, argv.ptr.compat + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
ksu_is_compat = true;
|
||||
return compat_ptr(compat);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (get_user(native, argv.ptr.native + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return native;
|
||||
}
|
||||
|
||||
/*
|
||||
* count() counts the number of strings in array ARGV.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Make sure old GCC compiler can use __maybe_unused,
|
||||
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||
*/
|
||||
|
||||
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (argv.ptr.native != NULL) {
|
||||
for (;;) {
|
||||
const char __user *p = get_user_arg_ptr(argv, i);
|
||||
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
if (IS_ERR(p))
|
||||
return -EFAULT;
|
||||
|
||||
if (i >= max)
|
||||
return -E2BIG;
|
||||
++i;
|
||||
|
||||
if (fatal_signal_pending(current))
|
||||
return -ERESTARTNOHAND;
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
struct user_arg_ptr *argv,
|
||||
struct user_arg_ptr *envp, int *flags)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct filename *filename;
|
||||
|
||||
static const char app_process[] = "/system/bin/app_process";
|
||||
static bool first_app_process = true;
|
||||
|
||||
/* This applies to versions Android 10+ */
|
||||
static const char system_bin_init[] = "/system/bin/init";
|
||||
/* This applies to versions between Android 6 ~ 9 */
|
||||
static const char old_system_init[] = "/init";
|
||||
static bool init_second_stage_executed = false;
|
||||
|
||||
if (!filename_ptr)
|
||||
return 0;
|
||||
|
||||
filename = *filename_ptr;
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1) &&
|
||||
argv)) {
|
||||
// /system/bin/init executed
|
||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||
pr_info("/system/bin/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
ksu_strncpy_from_user_retry(
|
||||
first_arg, p, sizeof(first_arg));
|
||||
pr_info("/system/bin/init first arg: %s\n",
|
||||
first_arg);
|
||||
if (!strcmp(first_arg, "second_stage")) {
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
ksu_apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
} else {
|
||||
pr_err("/system/bin/init parse args err!\n");
|
||||
}
|
||||
}
|
||||
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
||||
sizeof(old_system_init) - 1) &&
|
||||
argv)) {
|
||||
// /init executed
|
||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||
pr_info("/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
/* This applies to versions between Android 6 ~ 7 */
|
||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
ksu_strncpy_from_user_retry(
|
||||
first_arg, p, sizeof(first_arg));
|
||||
pr_info("/init first arg: %s\n", first_arg);
|
||||
if (!strcmp(first_arg, "--second-stage")) {
|
||||
pr_info("/init second_stage executed\n");
|
||||
ksu_apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
} else {
|
||||
pr_err("/init parse args err!\n");
|
||||
}
|
||||
} else if (argc == 1 && !init_second_stage_executed && envp) {
|
||||
/* This applies to versions between Android 8 ~ 9 */
|
||||
int envc = count(*envp, MAX_ARG_STRINGS);
|
||||
if (envc > 0) {
|
||||
int n;
|
||||
for (n = 1; n <= envc; n++) {
|
||||
const char __user *p =
|
||||
get_user_arg_ptr(*envp, n);
|
||||
if (!p || IS_ERR(p)) {
|
||||
continue;
|
||||
}
|
||||
char env[256];
|
||||
// Reading environment variable strings from user space
|
||||
if (ksu_strncpy_from_user_retry(
|
||||
env, p, sizeof(env)) < 0)
|
||||
continue;
|
||||
// Parsing environment variable names and values
|
||||
char *env_name = env;
|
||||
char *env_value = strchr(env, '=');
|
||||
if (env_value == NULL)
|
||||
continue;
|
||||
// Replace equal sign with string terminator
|
||||
*env_value = '\0';
|
||||
env_value++;
|
||||
// Check if the environment variable name and value are matching
|
||||
if (!strcmp(env_name,
|
||||
"INIT_SECOND_STAGE") &&
|
||||
(!strcmp(env_value, "1") ||
|
||||
!strcmp(env_value, "true"))) {
|
||||
pr_info("/init second_stage executed\n");
|
||||
ksu_apply_kernelsu_rules();
|
||||
init_second_stage_executed =
|
||||
true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
||||
sizeof(app_process) - 1))) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||
init_second_stage_executed);
|
||||
ksu_on_post_fs_data(); // we keep this for old ksud
|
||||
stop_execve_hook();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||
static struct file_operations fops_proxy;
|
||||
static ssize_t read_count_append = 0;
|
||||
|
||||
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
bool first_read = file->f_pos == 0;
|
||||
ssize_t ret = orig_read(file, buf, count, pos);
|
||||
if (first_read) {
|
||||
pr_info("read_proxy append %ld + %ld\n", ret,
|
||||
read_count_append);
|
||||
ret += read_count_append;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
bool first_read = iocb->ki_pos == 0;
|
||||
ssize_t ret = orig_read_iter(iocb, to);
|
||||
if (first_read) {
|
||||
pr_info("read_iter_proxy append %ld + %ld\n", ret,
|
||||
read_count_append);
|
||||
ret += read_count_append;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_vfs_read_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct file *file;
|
||||
char __user *buf;
|
||||
size_t count;
|
||||
|
||||
if (strcmp(current->comm, "init")) {
|
||||
// we are only interest in `init` process
|
||||
return 0;
|
||||
}
|
||||
|
||||
file = *file_ptr;
|
||||
if (IS_ERR(file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!d_is_reg(file->f_path.dentry)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *short_name = file->f_path.dentry->d_name.name;
|
||||
if (strcmp(short_name, "atrace.rc")) {
|
||||
// we are only interest `atrace.rc` file name file
|
||||
return 0;
|
||||
}
|
||||
char path[256];
|
||||
char *dpath = d_path(&file->f_path, path, sizeof(path));
|
||||
|
||||
if (IS_ERR(dpath)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we only process the first read
|
||||
static bool rc_inserted = false;
|
||||
if (rc_inserted) {
|
||||
// we don't need this kprobe, unregister it!
|
||||
stop_vfs_read_hook();
|
||||
return 0;
|
||||
}
|
||||
rc_inserted = true;
|
||||
|
||||
// now we can sure that the init process is reading
|
||||
// `/system/etc/init/atrace.rc`
|
||||
buf = *buf_ptr;
|
||||
count = *count_ptr;
|
||||
|
||||
size_t rc_count = strlen(KERNEL_SU_RC);
|
||||
|
||||
pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath,
|
||||
current->comm, count, rc_count);
|
||||
|
||||
if (count < rc_count) {
|
||||
pr_err("count: %zu < rc_count: %zu\n", count, rc_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
|
||||
if (ret) {
|
||||
pr_err("copy ksud.rc failed: %zu\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we've succeed to insert ksud.rc, now we need to proxy the read and modify the result!
|
||||
// But, we can not modify the file_operations directly, because it's in read-only memory.
|
||||
// We just replace the whole file_operations with a proxy one.
|
||||
memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations));
|
||||
orig_read = file->f_op->read;
|
||||
if (orig_read) {
|
||||
fops_proxy.read = read_proxy;
|
||||
}
|
||||
orig_read_iter = file->f_op->read_iter;
|
||||
if (orig_read_iter) {
|
||||
fops_proxy.read_iter = read_iter_proxy;
|
||||
}
|
||||
// replace the file_operations
|
||||
file->f_op = &fops_proxy;
|
||||
read_count_append = rc_count;
|
||||
|
||||
*buf_ptr = buf + rc_count;
|
||||
*count_ptr = count - rc_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
||||
size_t *count_ptr)
|
||||
{
|
||||
struct file *file = fget(fd);
|
||||
if (!file) {
|
||||
return 0;
|
||||
}
|
||||
int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL);
|
||||
fput(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned int volumedown_pressed_count = 0;
|
||||
|
||||
static bool is_volumedown_enough(unsigned int count)
|
||||
{
|
||||
return count >= 3;
|
||||
}
|
||||
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_input_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
|
||||
int val = *value;
|
||||
pr_info("KEY_VOLUMEDOWN val: %d\n", val);
|
||||
if (val) {
|
||||
// key pressed, count it
|
||||
volumedown_pressed_count += 1;
|
||||
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||
stop_input_hook();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ksu_is_safe_mode()
|
||||
{
|
||||
static bool safe_mode = false;
|
||||
if (safe_mode) {
|
||||
// don't need to check again, userspace may call multiple times
|
||||
return true;
|
||||
}
|
||||
|
||||
// stop hook first!
|
||||
stop_input_hook();
|
||||
|
||||
pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count);
|
||||
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||
// pressed over 3 times
|
||||
pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n");
|
||||
safe_mode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||
struct filename **filename_ptr =
|
||||
(struct filename **)&PT_REGS_PARM2(regs);
|
||||
struct user_arg_ptr argv;
|
||||
#ifdef CONFIG_COMPAT
|
||||
argv.is_compat = PT_REGS_PARM3(regs);
|
||||
if (unlikely(argv.is_compat)) {
|
||||
argv.ptr.compat = PT_REGS_CCALL_PARM4(regs);
|
||||
} else {
|
||||
argv.ptr.native = PT_REGS_CCALL_PARM4(regs);
|
||||
}
|
||||
#else
|
||||
argv.ptr.native = PT_REGS_PARM3(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||
}
|
||||
|
||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM1(real_regs);
|
||||
const char __user *const __user *__argv =
|
||||
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct filename filename_in, *filename_p;
|
||||
char path[32];
|
||||
|
||||
if (!filename_user)
|
||||
return 0;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
||||
filename_in.name = path;
|
||||
|
||||
filename_p = &filename_in;
|
||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
unsigned int fd = PT_REGS_PARM1(real_regs);
|
||||
char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs);
|
||||
size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs);
|
||||
|
||||
return ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
||||
}
|
||||
|
||||
static int input_handle_event_handler_pre(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
||||
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
||||
int *value = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
return ksu_handle_input_handle_event(type, code, value);
|
||||
}
|
||||
|
||||
static struct kprobe execve_kp = {
|
||||
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||
.pre_handler = sys_execve_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe vfs_read_kp = {
|
||||
.symbol_name = SYS_READ_SYMBOL,
|
||||
.pre_handler = sys_read_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe input_event_kp = {
|
||||
.symbol_name = "input_event",
|
||||
.pre_handler = input_handle_event_handler_pre,
|
||||
};
|
||||
|
||||
static void do_stop_vfs_read_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&vfs_read_kp);
|
||||
}
|
||||
|
||||
static void do_stop_execve_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&execve_kp);
|
||||
}
|
||||
|
||||
static void do_stop_input_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&input_event_kp);
|
||||
}
|
||||
#else
|
||||
static int ksu_execve_ksud_common(const char __user *filename_user,
|
||||
struct user_arg_ptr *argv)
|
||||
{
|
||||
struct filename filename_in, *filename_p;
|
||||
char path[32];
|
||||
long len;
|
||||
|
||||
// return early if disabled.
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!filename_user)
|
||||
return 0;
|
||||
|
||||
len = ksu_strncpy_from_user_nofault(path, filename_user, 32);
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
// this is because ksu_handle_execveat_ksud calls it filename->name
|
||||
filename_in.name = path;
|
||||
filename_p = &filename_in;
|
||||
|
||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL, NULL);
|
||||
}
|
||||
|
||||
int __maybe_unused ksu_handle_execve_ksud(const char __user *filename_user,
|
||||
const char __user *const __user *__argv)
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
return ksu_execve_ksud_common(filename_user, &argv);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT)
|
||||
int __maybe_unused ksu_handle_compat_execve_ksud(const char __user *filename_user,
|
||||
const compat_uptr_t __user *__argv)
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.compat = __argv };
|
||||
return ksu_execve_ksud_common(filename_user, &argv);
|
||||
}
|
||||
#endif /* COMPAT & 64BIT */
|
||||
|
||||
#endif
|
||||
|
||||
static void stop_vfs_read_hook()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
#else
|
||||
ksu_vfs_read_hook = false;
|
||||
pr_info("stop vfs_read_hook\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stop_execve_hook()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
ksu_execveat_hook = false;
|
||||
pr_info("stop execve_hook\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stop_input_hook()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool input_hook_stopped = false;
|
||||
if (input_hook_stopped) {
|
||||
return;
|
||||
}
|
||||
input_hook_stopped = true;
|
||||
bool ret = schedule_work(&stop_input_hook_work);
|
||||
pr_info("unregister input kprobe: %d!\n", ret);
|
||||
#else
|
||||
if (!ksu_input_hook) { return; }
|
||||
ksu_input_hook = false;
|
||||
pr_info("stop input_hook\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// ksud: module support
|
||||
void ksu_ksud_init()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&execve_kp);
|
||||
pr_info("ksud: execve_kp: %d\n", ret);
|
||||
|
||||
ret = register_kprobe(&vfs_read_kp);
|
||||
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
||||
|
||||
ret = register_kprobe(&input_event_kp);
|
||||
pr_info("ksud: input_event_kp: %d\n", ret);
|
||||
|
||||
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
||||
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
||||
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_ksud_exit()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
unregister_kprobe(&execve_kp);
|
||||
// this should be done before unregister vfs_read_kp
|
||||
// unregister_kprobe(&vfs_read_kp);
|
||||
unregister_kprobe(&input_event_kp);
|
||||
#endif
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __KSU_H_KSUD
|
||||
#define __KSU_H_KSUD
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define KSUD_PATH "/data/adb/ksud"
|
||||
|
||||
void ksu_on_post_fs_data(void);
|
||||
|
||||
bool ksu_is_safe_mode(void);
|
||||
|
||||
extern u32 ksu_devpts_sid;
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef __KSU_H_KSU_MANAGER
|
||||
#define __KSU_H_KSU_MANAGER
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define KSU_INVALID_UID -1
|
||||
|
||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid()
|
||||
{
|
||||
return ksu_manager_uid != KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
static inline bool ksu_is_manager()
|
||||
{
|
||||
return unlikely(ksu_manager_uid == current_uid().val);
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
{
|
||||
return ksu_manager_uid;
|
||||
}
|
||||
|
||||
static inline void ksu_set_manager_uid(uid_t uid)
|
||||
{
|
||||
ksu_manager_uid = uid;
|
||||
}
|
||||
|
||||
static inline void ksu_invalidate_manager_uid()
|
||||
{
|
||||
ksu_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef __KSU_H_MANAGER_SIGN
|
||||
#define __KSU_H_MANAGER_SIGN
|
||||
|
||||
// rsuntk/KernelSU
|
||||
#define EXPECTED_SIZE_RSUNTK 0x396
|
||||
#define EXPECTED_HASH_RSUNTK "f415f4ed9435427e1fdf7f1fccd4dbc07b3d6b8751e4dbcec6f19671f427870b"
|
||||
|
||||
// 5ec1cff/KernelSU
|
||||
#define EXPECTED_SIZE_5EC1CFF 384
|
||||
#define EXPECTED_HASH_5EC1CFF "7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4"
|
||||
|
||||
// tiann/KernelSU
|
||||
#define EXPECTED_SIZE_OFFICIAL 0x033b
|
||||
#define EXPECTED_HASH_OFFICIAL "c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6"
|
||||
|
||||
// ksu-next
|
||||
#define EXPECTED_SIZE_NEXT 0x3e6
|
||||
#define EXPECTED_HASH_NEXT "79e590113c4c4c0c222978e413a5faa801666957b1212a328e46c00c69821bf7"
|
||||
|
||||
#endif /* MANAGER_SIGN_H */
|
||||
@@ -1,542 +0,0 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#include "selinux.h"
|
||||
#include "sepolicy.h"
|
||||
#include "ss/services.h"
|
||||
#include "linux/lsm_audit.h"
|
||||
#include "xfrm.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_DOMAIN "su"
|
||||
#define KERNEL_SU_FILE "ksu_file"
|
||||
#define KERNEL_EXEC_TYPE "ksu_exec"
|
||||
#define ALL NULL
|
||||
|
||||
static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
// selinux_state does not exists before 4.19
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||
db = &policy->policydb;
|
||||
#else
|
||||
struct selinux_ss *ss = rcu_dereference(selinux_state.ss);
|
||||
db = &ss->policydb;
|
||||
#endif
|
||||
#else
|
||||
db = &policydb;
|
||||
#endif
|
||||
return db;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(apply_ksu_rules_mutex);
|
||||
void ksu_apply_kernelsu_rules()
|
||||
{
|
||||
struct policydb *db;
|
||||
|
||||
if (!ksu_getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||
}
|
||||
|
||||
mutex_lock(&apply_ksu_rules_mutex);
|
||||
|
||||
db = get_policydb();
|
||||
|
||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||
|
||||
// Create unconstrained file type
|
||||
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||
|
||||
// allow all!
|
||||
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||
|
||||
// allow us do any ioctl
|
||||
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
||||
}
|
||||
|
||||
// we need to save allowlist in /data/adb/ksu
|
||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||
// we need to search /data/app
|
||||
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||
// we may need to do mount on shell
|
||||
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||
// we need to read /data/system/packages.list
|
||||
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
||||
// Android 10+:
|
||||
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
||||
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
||||
// Kernel 4.4
|
||||
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
||||
// Android 9-:
|
||||
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
||||
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
||||
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
||||
// our ksud triggered by init
|
||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||
|
||||
// we need to umount modules in zygote
|
||||
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||
|
||||
// copied from Magisk rules
|
||||
// suRights
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||
|
||||
// allowLog
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||
|
||||
// dumpsys
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||
|
||||
// bootctl
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||
"getattr");
|
||||
|
||||
// For mounting loop devices, mirrors, tmpfs
|
||||
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||
|
||||
// Allow all binder transactions
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||
|
||||
// Allow system server kill su process
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
// Allow umount in zygote process without installing zygisk
|
||||
ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount");
|
||||
susfs_set_init_sid();
|
||||
susfs_set_ksu_sid();
|
||||
susfs_set_zygote_sid();
|
||||
#endif
|
||||
|
||||
mutex_unlock(&apply_ksu_rules_mutex);
|
||||
}
|
||||
|
||||
#define MAX_SEPOL_LEN 128
|
||||
|
||||
#define CMD_NORMAL_PERM 1
|
||||
#define CMD_XPERM 2
|
||||
#define CMD_TYPE_STATE 3
|
||||
#define CMD_TYPE 4
|
||||
#define CMD_TYPE_ATTR 5
|
||||
#define CMD_ATTR 6
|
||||
#define CMD_TYPE_TRANSITION 7
|
||||
#define CMD_TYPE_CHANGE 8
|
||||
#define CMD_GENFSCON 9
|
||||
|
||||
// keep it!
|
||||
extern bool ksu_is_compat __read_mostly;
|
||||
|
||||
// armv7l kernel compat
|
||||
#ifdef CONFIG_64BIT
|
||||
#define usize u64
|
||||
#else
|
||||
#define usize u32
|
||||
#endif
|
||||
|
||||
struct sepol_data {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
usize field_sepol1;
|
||||
usize field_sepol2;
|
||||
usize field_sepol3;
|
||||
usize field_sepol4;
|
||||
usize field_sepol5;
|
||||
usize field_sepol6;
|
||||
usize field_sepol7;
|
||||
};
|
||||
|
||||
// ksud 32-bit on arm64 kernel
|
||||
struct __maybe_unused sepol_data_compat {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
u32 field_sepol1;
|
||||
u32 field_sepol2;
|
||||
u32 field_sepol3;
|
||||
u32 field_sepol4;
|
||||
u32 field_sepol5;
|
||||
u32 field_sepol6;
|
||||
u32 field_sepol7;
|
||||
};
|
||||
|
||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
char **object)
|
||||
{
|
||||
if (!user_object) {
|
||||
*object = ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*object = buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||
static void reset_avc_cache()
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||
avc_ss_reset(0);
|
||||
selnl_notify_policyload(0);
|
||||
selinux_status_update_policyload(0);
|
||||
#else
|
||||
struct selinux_avc *avc = selinux_state.avc;
|
||||
avc_ss_reset(avc, 0);
|
||||
selnl_notify_policyload(0);
|
||||
selinux_status_update_policyload(&selinux_state, 0);
|
||||
#endif
|
||||
selinux_xfrm_notify_policyload();
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(ksu_handle_sepolicy_mutex);
|
||||
int ksu_handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
{
|
||||
if (!arg4) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ksu_getenforce()) {
|
||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||
}
|
||||
|
||||
u32 cmd, subcmd;
|
||||
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
||||
|
||||
if (unlikely(ksu_is_compat)) {
|
||||
struct sepol_data_compat data_compat;
|
||||
if (copy_from_user(&data_compat, arg4, sizeof(struct sepol_data_compat))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
pr_info("sepol: running in compat mode!\n");
|
||||
sepol1 = compat_ptr(data_compat.field_sepol1);
|
||||
sepol2 = compat_ptr(data_compat.field_sepol2);
|
||||
sepol3 = compat_ptr(data_compat.field_sepol3);
|
||||
sepol4 = compat_ptr(data_compat.field_sepol4);
|
||||
sepol5 = compat_ptr(data_compat.field_sepol5);
|
||||
sepol6 = compat_ptr(data_compat.field_sepol6);
|
||||
sepol7 = compat_ptr(data_compat.field_sepol7);
|
||||
cmd = data_compat.cmd;
|
||||
subcmd = data_compat.subcmd;
|
||||
} else {
|
||||
struct sepol_data data;
|
||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
sepol1 = data.field_sepol1;
|
||||
sepol2 = data.field_sepol2;
|
||||
sepol3 = data.field_sepol3;
|
||||
sepol4 = data.field_sepol4;
|
||||
sepol5 = data.field_sepol5;
|
||||
sepol6 = data.field_sepol6;
|
||||
sepol7 = data.field_sepol7;
|
||||
cmd = data.cmd;
|
||||
subcmd = data.subcmd;
|
||||
}
|
||||
|
||||
struct policydb *db;
|
||||
|
||||
mutex_lock(&ksu_handle_sepolicy_mutex);
|
||||
|
||||
db = get_policydb();
|
||||
|
||||
int ret = -1;
|
||||
if (cmd == CMD_NORMAL_PERM) {
|
||||
char src_buf[MAX_SEPOL_LEN];
|
||||
char tgt_buf[MAX_SEPOL_LEN];
|
||||
char cls_buf[MAX_SEPOL_LEN];
|
||||
char perm_buf[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c, *p;
|
||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_allow(db, s, t, c, p);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_deny(db, s, t, c, p);
|
||||
} else if (subcmd == 3) {
|
||||
success = ksu_auditallow(db, s, t, c, p);
|
||||
} else if (subcmd == 4) {
|
||||
success = ksu_dontaudit(db, s, t, c, p);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||
}
|
||||
ret = success ? 0 : -1;
|
||||
|
||||
} else if (cmd == CMD_XPERM) {
|
||||
char src_buf[MAX_SEPOL_LEN];
|
||||
char tgt_buf[MAX_SEPOL_LEN];
|
||||
char cls_buf[MAX_SEPOL_LEN];
|
||||
|
||||
char __maybe_unused
|
||||
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||
char perm_set[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c;
|
||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(operation, sepol4,
|
||||
sizeof(operation)) < 0) {
|
||||
pr_err("sepol: copy operation failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm_set failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_allowxperm(db, s, t, c, perm_set);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
||||
} else if (subcmd == 3) {
|
||||
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||
}
|
||||
ret = success ? 0 : -1;
|
||||
} else if (cmd == CMD_TYPE_STATE) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_permissive(db, src);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_enforce(db, src);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||
}
|
||||
if (success)
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||
char type[MAX_SEPOL_LEN];
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
||||
pr_err("sepol: copy type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (cmd == CMD_TYPE) {
|
||||
success = ksu_type(db, type, attr);
|
||||
} else {
|
||||
success = ksu_typeattribute(db, type, attr);
|
||||
}
|
||||
if (!success) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_ATTR) {
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (!ksu_attribute(db, attr)) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
char tgt[MAX_SEPOL_LEN];
|
||||
char cls[MAX_SEPOL_LEN];
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
char object[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
char *real_object;
|
||||
if (sepol5 == NULL) {
|
||||
real_object = NULL;
|
||||
} else {
|
||||
if (strncpy_from_user(object, sepol5,
|
||||
sizeof(object)) < 0) {
|
||||
pr_err("sepol: copy object failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
real_object = object;
|
||||
}
|
||||
|
||||
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||
default_type, real_object);
|
||||
if (success)
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
char tgt[MAX_SEPOL_LEN];
|
||||
char cls[MAX_SEPOL_LEN];
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_type_change(db, src, tgt, cls,
|
||||
default_type);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_type_member(db, src, tgt, cls,
|
||||
default_type);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||
}
|
||||
if (success)
|
||||
ret = 0;
|
||||
} else if (cmd == CMD_GENFSCON) {
|
||||
char name[MAX_SEPOL_LEN];
|
||||
char path[MAX_SEPOL_LEN];
|
||||
char context[MAX_SEPOL_LEN];
|
||||
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
||||
pr_err("sepol: copy name failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
||||
pr_err("sepol: copy path failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
||||
0) {
|
||||
pr_err("sepol: copy context failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!ksu_genfscon(db, name, path, context)) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ksu_handle_sepolicy_mutex);
|
||||
|
||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||
// we are in atomic context. so we just reset it every time.
|
||||
reset_avc_cache();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
#include "selinux.h"
|
||||
#include "objsec.h"
|
||||
#include "linux/version.h"
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#ifdef SAMSUNG_SELINUX_PORTING
|
||||
#include "security.h" // Samsung SELinux Porting
|
||||
#endif
|
||||
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#include "avc.h"
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
#define KERNEL_INIT_DOMAIN "u:r:init:s0"
|
||||
#define KERNEL_ZYGOTE_DOMAIN "u:r:zygote:s0"
|
||||
u32 susfs_ksu_sid = 0;
|
||||
u32 susfs_init_sid = 0;
|
||||
u32 susfs_zygote_sid = 0;
|
||||
#endif
|
||||
|
||||
static int transive_to_domain(const char *domain)
|
||||
{
|
||||
struct cred *cred;
|
||||
struct task_security_struct *tsec;
|
||||
u32 sid;
|
||||
int error;
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
tsec = cred->security;
|
||||
if (!tsec) {
|
||||
pr_err("tsec == NULL!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||
if (error) {
|
||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||
domain, sid, error);
|
||||
}
|
||||
if (!error) {
|
||||
tsec->sid = sid;
|
||||
tsec->create_sid = 0;
|
||||
tsec->keycreate_sid = 0;
|
||||
tsec->sockcreate_sid = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
bool __maybe_unused is_ksu_transition(const struct task_security_struct *old_tsec,
|
||||
const struct task_security_struct *new_tsec)
|
||||
{
|
||||
static u32 ksu_sid;
|
||||
char *secdata;
|
||||
u32 seclen;
|
||||
bool allowed = false;
|
||||
|
||||
if (!ksu_sid)
|
||||
security_secctx_to_secid("u:r:su:s0", strlen("u:r:su:s0"), &ksu_sid);
|
||||
|
||||
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
||||
return false;
|
||||
|
||||
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
||||
security_release_secctx(secdata, seclen);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
void ksu_setup_selinux(const char *domain)
|
||||
{
|
||||
if (transive_to_domain(domain)) {
|
||||
pr_err("transive domain failed.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_setenforce(bool enforce)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#ifdef SAMSUNG_SELINUX_PORTING
|
||||
selinux_enforcing = enforce;
|
||||
#endif
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
selinux_state.enforcing = enforce;
|
||||
#else
|
||||
selinux_enforcing = enforce;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ksu_getenforce()
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
if (selinux_state.disabled) {
|
||||
#else
|
||||
if (selinux_disabled) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#ifdef SAMSUNG_SELINUX_PORTING
|
||||
return selinux_enforcing;
|
||||
#endif
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
return selinux_state.enforcing;
|
||||
#else
|
||||
return selinux_enforcing;
|
||||
#endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
/*
|
||||
* get the subjective security ID of the current task
|
||||
*/
|
||||
static inline u32 current_sid(void)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
return tsec->sid;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ksu_is_ksu_domain()
|
||||
{
|
||||
char *domain;
|
||||
u32 seclen;
|
||||
bool result;
|
||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
||||
security_release_secctx(domain, seclen);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ksu_is_zygote(void *sec)
|
||||
{
|
||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||
if (!tsec) {
|
||||
return false;
|
||||
}
|
||||
char *domain;
|
||||
u32 seclen;
|
||||
bool result;
|
||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
||||
security_release_secctx(domain, seclen);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!secctx_name || !out_sid) {
|
||||
pr_err("secctx_name || out_sid is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||
out_sid);
|
||||
if (err) {
|
||||
pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err);
|
||||
return;
|
||||
}
|
||||
pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name);
|
||||
}
|
||||
|
||||
bool susfs_is_sid_equal(void *sec, u32 sid2) {
|
||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||
if (!tsec) {
|
||||
return false;
|
||||
}
|
||||
return tsec->sid == sid2;
|
||||
}
|
||||
|
||||
u32 susfs_get_sid_from_name(const char *secctx_name)
|
||||
{
|
||||
u32 out_sid = 0;
|
||||
int err;
|
||||
|
||||
if (!secctx_name) {
|
||||
pr_err("secctx_name is NULL\n");
|
||||
return 0;
|
||||
}
|
||||
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||
&out_sid);
|
||||
if (err) {
|
||||
pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err);
|
||||
return 0;
|
||||
}
|
||||
return out_sid;
|
||||
}
|
||||
|
||||
u32 susfs_get_current_sid(void) {
|
||||
return current_sid();
|
||||
}
|
||||
|
||||
void susfs_set_zygote_sid(void)
|
||||
{
|
||||
susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid);
|
||||
}
|
||||
|
||||
bool susfs_is_current_zygote_domain(void) {
|
||||
return unlikely(current_sid() == susfs_zygote_sid);
|
||||
}
|
||||
|
||||
void susfs_set_ksu_sid(void)
|
||||
{
|
||||
susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid);
|
||||
}
|
||||
|
||||
bool susfs_is_current_ksu_domain(void) {
|
||||
return unlikely(current_sid() == susfs_ksu_sid);
|
||||
}
|
||||
|
||||
void susfs_set_init_sid(void)
|
||||
{
|
||||
susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid);
|
||||
}
|
||||
|
||||
bool susfs_is_current_init_domain(void) {
|
||||
return unlikely(current_sid() == susfs_init_sid);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
||||
|
||||
u32 ksu_get_devpts_sid()
|
||||
{
|
||||
u32 devpts_sid = 0;
|
||||
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
||||
&devpts_sid);
|
||||
if (err) {
|
||||
pr_info("get devpts sid err %d\n", err);
|
||||
}
|
||||
return devpts_sid;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef __KSU_H_SELINUX
|
||||
#define __KSU_H_SELINUX
|
||||
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||
#endif
|
||||
|
||||
void ksu_setup_selinux(const char *);
|
||||
|
||||
void ksu_setenforce(bool);
|
||||
|
||||
bool ksu_getenforce();
|
||||
|
||||
bool ksu_is_ksu_domain();
|
||||
|
||||
bool ksu_is_zygote(void *cred);
|
||||
|
||||
void ksu_apply_kernelsu_rules();
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
bool susfs_is_sid_equal(void *sec, u32 sid2);
|
||||
u32 susfs_get_sid_from_name(const char *secctx_name);
|
||||
u32 susfs_get_current_sid(void);
|
||||
void susfs_set_zygote_sid(void);
|
||||
bool susfs_is_current_zygote_domain(void);
|
||||
void susfs_set_ksu_sid(void);
|
||||
bool susfs_is_current_ksu_domain(void);
|
||||
void susfs_set_init_sid(void);
|
||||
bool susfs_is_current_init_domain(void);
|
||||
#endif
|
||||
|
||||
u32 ksu_get_devpts_sid();
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +0,0 @@
|
||||
#ifndef __KSU_H_SEPOLICY
|
||||
#define __KSU_H_SEPOLICY
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ss/policydb.h"
|
||||
|
||||
// Operation on types
|
||||
bool ksu_type(struct policydb *db, const char *name, const char *attr);
|
||||
bool ksu_attribute(struct policydb *db, const char *name);
|
||||
bool ksu_permissive(struct policydb *db, const char *type);
|
||||
bool ksu_enforce(struct policydb *db, const char *type);
|
||||
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr);
|
||||
bool ksu_exists(struct policydb *db, const char *type);
|
||||
|
||||
// Access vector rules
|
||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
|
||||
// Extended permissions access vector rules
|
||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
|
||||
// Type rules
|
||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def, const char *obj);
|
||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def);
|
||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def);
|
||||
|
||||
// File system labeling
|
||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||
const char *ctx);
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
GKI_ROOT=$(pwd)
|
||||
|
||||
display_usage() {
|
||||
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
||||
echo " --cleanup: Cleans up previous modifications made by the script."
|
||||
echo " <commit-or-tag>: Sets up or updates the KernelSU to specified tag or commit."
|
||||
echo " -h, --help: Displays this usage information."
|
||||
echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version."
|
||||
}
|
||||
|
||||
initialize_variables() {
|
||||
if test -d "$GKI_ROOT/common/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
||||
elif test -d "$GKI_ROOT/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||
else
|
||||
echo '[ERROR] "drivers/" directory not found.'
|
||||
exit 127
|
||||
fi
|
||||
|
||||
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
|
||||
}
|
||||
|
||||
# Reverts modifications made by this script
|
||||
perform_cleanup() {
|
||||
echo "[+] Cleaning up..."
|
||||
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
||||
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Sets up or update KernelSU environment
|
||||
setup_kernelsu() {
|
||||
echo "[+] Setting up KernelSU..."
|
||||
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/rsuntk/KernelSU && echo "[+] Repository cloned."
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
git stash && echo "[-] Stashed current changes."
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
git checkout main && echo "[-] Switched to main branch."
|
||||
fi
|
||||
git pull && echo "[+] Repository updated."
|
||||
if [ -z "${1-}" ]; then
|
||||
git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag."
|
||||
else
|
||||
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||
fi
|
||||
cd "$DRIVER_DIR"
|
||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||
|
||||
# Add entries in Makefile and Kconfig if not already existing
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||
echo '[+] Done.'
|
||||
}
|
||||
|
||||
# Process command-line arguments
|
||||
if [ "$#" -eq 0 ]; then
|
||||
initialize_variables
|
||||
setup_kernelsu
|
||||
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
display_usage
|
||||
elif [ "$1" = "--cleanup" ]; then
|
||||
initialize_variables
|
||||
perform_cleanup
|
||||
else
|
||||
initialize_variables
|
||||
setup_kernelsu "$@"
|
||||
fi
|
||||
@@ -1,307 +0,0 @@
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/security.h>
|
||||
#include <asm/current.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/ptrace.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/sched/task_stack.h>
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
|
||||
#include "objsec.h"
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#define SU_PATH "/system/bin/su"
|
||||
#define SH_PATH "/system/bin/sh"
|
||||
|
||||
static const char su[] = SU_PATH;
|
||||
static const char ksud_path[] = KSUD_PATH;
|
||||
|
||||
extern void ksu_escape_to_root();
|
||||
|
||||
bool ksu_sucompat_hook_state __read_mostly = true;
|
||||
|
||||
static inline void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||
{
|
||||
/* To avoid having to mmap a page in userspace, just write below the stack
|
||||
* pointer. */
|
||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||
|
||||
return copy_to_user(p, d, len) ? NULL : p;
|
||||
}
|
||||
|
||||
static inline char __user *sh_user_path(void)
|
||||
{
|
||||
const char sh_path[] = SH_PATH;
|
||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||
}
|
||||
|
||||
static inline char __user *ksud_user_path(void)
|
||||
{
|
||||
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||
}
|
||||
|
||||
static inline bool __is_su_allowed(const void *ptr_to_check)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_hook_state)
|
||||
return false;
|
||||
#endif
|
||||
if (likely(!ksu_is_allow_uid(current_uid().val)))
|
||||
return false;
|
||||
|
||||
if (unlikely(!ptr_to_check))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#define is_su_allowed(ptr) __is_su_allowed((const void *)ptr)
|
||||
|
||||
static int ksu_sucompat_user_common(const char __user **filename_user,
|
||||
const char *syscall_name,
|
||||
const bool escalate)
|
||||
{
|
||||
char path[sizeof(su)]; // sizeof includes nullterm already!
|
||||
if (ksu_strncpy_from_user_retry(path,
|
||||
*filename_user, sizeof(path)) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
if (memcmp(path, su, sizeof(su)))
|
||||
return 0;
|
||||
|
||||
if (escalate) {
|
||||
pr_info("%s su found\n", syscall_name);
|
||||
*filename_user = ksud_user_path();
|
||||
ksu_escape_to_root(); // escalate !!
|
||||
} else {
|
||||
pr_info("%s su->sh!\n", syscall_name);
|
||||
*filename_user = sh_user_path();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *__unused_flags)
|
||||
{
|
||||
if (!is_su_allowed(filename_user))
|
||||
return 0;
|
||||
|
||||
return ksu_sucompat_user_common(filename_user, "faccessat", false);
|
||||
}
|
||||
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
{
|
||||
if (!is_su_allowed(filename_user))
|
||||
return 0;
|
||||
|
||||
return ksu_sucompat_user_common(filename_user, "newfstatat", false);
|
||||
}
|
||||
|
||||
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
void *__never_use_argv, void *__never_use_envp,
|
||||
int *__never_use_flags)
|
||||
{
|
||||
if (!is_su_allowed(filename_user))
|
||||
return 0;
|
||||
|
||||
return ksu_sucompat_user_common(filename_user, "sys_execve", true);
|
||||
}
|
||||
|
||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *__never_use_argv, void *__never_use_envp,
|
||||
int *__never_use_flags)
|
||||
{
|
||||
struct filename *filename;
|
||||
|
||||
if (!is_su_allowed(filename_ptr))
|
||||
return 0;
|
||||
|
||||
filename = *filename_ptr;
|
||||
if (IS_ERR(filename))
|
||||
return 0;
|
||||
|
||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||
return 0;
|
||||
|
||||
pr_info("do_execveat_common su found\n");
|
||||
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
||||
|
||||
ksu_escape_to_root();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksu_inline_handle_devpts(struct inode *inode)
|
||||
{
|
||||
if (!current->mm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uid_t uid = current_uid().val;
|
||||
if (uid % 100000 < 10000) {
|
||||
// not untrusted_app, ignore it
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_is_allow_uid(uid))
|
||||
return 0;
|
||||
|
||||
if (ksu_devpts_sid) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||
struct inode_security_struct *sec = selinux_inode(inode);
|
||||
#else
|
||||
struct inode_security_struct *sec =
|
||||
(struct inode_security_struct *)inode->i_security;
|
||||
#endif
|
||||
if (sec) {
|
||||
sec->sid = ksu_devpts_sid;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_hook_state)
|
||||
return 0;
|
||||
#endif
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
}
|
||||
|
||||
// dead code, we are phasing out ksu_handle_devpts for LSM hooks.
|
||||
int __maybe_unused ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM2(real_regs);
|
||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
||||
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||
}
|
||||
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM2(real_regs);
|
||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM1(real_regs);
|
||||
|
||||
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static struct kprobe *su_kps[6];
|
||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
||||
inode = file->f_path.dentry->d_inode;
|
||||
#else
|
||||
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||
#endif
|
||||
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
}
|
||||
#else
|
||||
static struct kprobe *su_kps[5];
|
||||
#endif
|
||||
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
{
|
||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||
if (!kp)
|
||||
return NULL;
|
||||
kp->symbol_name = name;
|
||||
kp->pre_handler = handler;
|
||||
|
||||
int ret = register_kprobe(kp);
|
||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||
if (ret) {
|
||||
kfree(kp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
{
|
||||
struct kprobe *kp = *kp_ptr;
|
||||
if (!kp)
|
||||
return;
|
||||
unregister_kprobe(kp);
|
||||
synchronize_rcu();
|
||||
kfree(kp);
|
||||
*kp_ptr = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[3] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre);
|
||||
#ifdef MODULE
|
||||
su_kps[5] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||
#endif
|
||||
#else
|
||||
ksu_sucompat_hook_state = true;
|
||||
pr_info("ksu_sucompat init\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
#else
|
||||
ksu_sucompat_hook_state = false;
|
||||
pr_info("ksu_sucompat exit\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "manager.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||
|
||||
struct uid_data {
|
||||
struct list_head list;
|
||||
u32 uid;
|
||||
char package[KSU_MAX_PACKAGE_NAME];
|
||||
};
|
||||
|
||||
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
if (len >= KSU_MAX_PACKAGE_NAME || len < 1)
|
||||
return -1;
|
||||
|
||||
const char *last_slash = NULL;
|
||||
const char *second_last_slash = NULL;
|
||||
|
||||
int i;
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
if (!last_slash) {
|
||||
last_slash = &path[i];
|
||||
} else {
|
||||
second_last_slash = &path[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!last_slash || !second_last_slash)
|
||||
return -1;
|
||||
|
||||
const char *last_hyphen = strchr(second_last_slash, '-');
|
||||
if (!last_hyphen || last_hyphen > last_slash)
|
||||
return -1;
|
||||
|
||||
int pkg_len = last_hyphen - second_last_slash - 1;
|
||||
if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0)
|
||||
return -1;
|
||||
|
||||
// Copying the package name
|
||||
strncpy(pkg, second_last_slash + 1, pkg_len);
|
||||
pkg[pkg_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||
{
|
||||
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||
pr_err("Failed to get package name from apk path: %s\n", apk);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("manager pkg: %s\n", pkg);
|
||||
|
||||
#ifdef KSU_MANAGER_PACKAGE
|
||||
// pkg is `/<real package>`
|
||||
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
|
||||
pr_info("manager package is inconsistent with kernel build: %s\n",
|
||||
KSU_MANAGER_PACKAGE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
struct list_head *list = (struct list_head *)uid_data;
|
||||
struct uid_data *np;
|
||||
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
||||
ksu_set_manager_uid(np->uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define DATA_PATH_LEN 384 // 384 is enough for /data/app/<package>/base.apk
|
||||
|
||||
struct data_path {
|
||||
char dirpath[DATA_PATH_LEN];
|
||||
int depth;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct apk_path_hash {
|
||||
unsigned int hash;
|
||||
bool exists;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static struct list_head apk_path_hash_list;
|
||||
|
||||
struct my_dir_context {
|
||||
struct dir_context ctx;
|
||||
struct list_head *data_path_list;
|
||||
char *parent_dir;
|
||||
void *private_data;
|
||||
int depth;
|
||||
int *stop;
|
||||
};
|
||||
// https://docs.kernel.org/filesystems/porting.html
|
||||
// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted.
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
#define FILLDIR_RETURN_TYPE bool
|
||||
#define FILLDIR_ACTOR_CONTINUE true
|
||||
#define FILLDIR_ACTOR_STOP false
|
||||
#else
|
||||
#define FILLDIR_RETURN_TYPE int
|
||||
#define FILLDIR_ACTOR_CONTINUE 0
|
||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||
#endif
|
||||
|
||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
int namelen, loff_t off, u64 ino,
|
||||
unsigned int d_type)
|
||||
{
|
||||
struct my_dir_context *my_ctx =
|
||||
container_of(ctx, struct my_dir_context, ctx);
|
||||
char dirpath[DATA_PATH_LEN];
|
||||
|
||||
if (!my_ctx) {
|
||||
pr_err("Invalid context\n");
|
||||
return FILLDIR_ACTOR_STOP;
|
||||
}
|
||||
if (my_ctx->stop && *my_ctx->stop) {
|
||||
pr_info("Stop searching\n");
|
||||
return FILLDIR_ACTOR_STOP;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||
|
||||
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
||||
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
||||
pr_info("Skipping directory: %.*s\n", namelen, name);
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||
}
|
||||
|
||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||
namelen, name) >= DATA_PATH_LEN) {
|
||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||
name);
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||
(my_ctx->stop && !*my_ctx->stop)) {
|
||||
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||
|
||||
if (!data) {
|
||||
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
strscpy(data->dirpath, dirpath, DATA_PATH_LEN);
|
||||
data->depth = my_ctx->depth - 1;
|
||||
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||
} else {
|
||||
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
||||
struct apk_path_hash *pos;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||
#else
|
||||
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||
#endif
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
if (hash == pos->hash) {
|
||||
pos->exists = true;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_manager = ksu_is_manager_apk(dirpath);
|
||||
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
||||
dirpath, is_manager);
|
||||
if (is_manager) {
|
||||
crown_manager(dirpath, my_ctx->private_data);
|
||||
*my_ctx->stop = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
{
|
||||
int i, stop = 0;
|
||||
struct list_head data_path_list;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
INIT_LIST_HEAD(&apk_path_hash_list);
|
||||
unsigned long data_app_magic = 0;
|
||||
|
||||
// Initialize APK cache list
|
||||
struct apk_path_hash *pos, *n;
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
pos->exists = false;
|
||||
}
|
||||
|
||||
// First depth
|
||||
struct data_path data;
|
||||
strscpy(data.dirpath, path, DATA_PATH_LEN);
|
||||
data.depth = depth;
|
||||
list_add_tail(&data.list, &data_path_list);
|
||||
|
||||
for (i = depth; i >= 0; i--) {
|
||||
struct data_path *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||
.data_path_list = &data_path_list,
|
||||
.parent_dir = pos->dirpath,
|
||||
.private_data = uid_data,
|
||||
.depth = pos->depth,
|
||||
.stop = &stop };
|
||||
struct file *file;
|
||||
|
||||
if (!stop) {
|
||||
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||
if (IS_ERR(file)) {
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
// grab magic on first folder, which is /data/app
|
||||
if (!data_app_magic) {
|
||||
if (file->f_inode->i_sb->s_magic) {
|
||||
data_app_magic = file->f_inode->i_sb->s_magic;
|
||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic);
|
||||
} else {
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath,
|
||||
file->f_inode->i_sb->s_magic, data_app_magic);
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
}
|
||||
skip_iterate:
|
||||
list_del(&pos->list);
|
||||
if (pos != &data)
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// clear apk_path_hash_list unconditionally
|
||||
pr_info("search manager: cleanup!\n");
|
||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
{
|
||||
struct list_head *list = (struct list_head *)data;
|
||||
struct uid_data *np;
|
||||
|
||||
bool exist = false;
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (np->uid == uid % 100000 &&
|
||||
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
void ksu_track_throne()
|
||||
{
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||
__func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
struct list_head uid_list;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
|
||||
char chr = 0;
|
||||
loff_t pos = 0;
|
||||
loff_t line_start = 0;
|
||||
char buf[KSU_MAX_PACKAGE_NAME];
|
||||
for (;;) {
|
||||
ssize_t count =
|
||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
||||
&line_start);
|
||||
|
||||
struct uid_data *data =
|
||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (!data) {
|
||||
filp_close(fp, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
char *tmp = buf;
|
||||
const char *delim = " ";
|
||||
char *package = strsep(&tmp, delim);
|
||||
char *uid = strsep(&tmp, delim);
|
||||
if (!uid || !package) {
|
||||
pr_err("update_uid: package or uid is NULL!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
u32 res;
|
||||
if (kstrtou32(uid, 10, &res)) {
|
||||
pr_err("update_uid: uid parse err\n");
|
||||
break;
|
||||
}
|
||||
data->uid = res;
|
||||
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
||||
list_add_tail(&data->list, &uid_list);
|
||||
// reset line start
|
||||
line_start = pos;
|
||||
}
|
||||
filp_close(fp, 0);
|
||||
|
||||
// now update uid list
|
||||
struct uid_data *np;
|
||||
struct uid_data *n;
|
||||
|
||||
// first, check if manager_uid exist!
|
||||
bool manager_exist = false;
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||
// don't delete it in this case!
|
||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||
if (np->uid == manager_uid) {
|
||||
manager_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!manager_exist) {
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
ksu_invalidate_manager_uid();
|
||||
goto prune;
|
||||
}
|
||||
pr_info("Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Search manager finished\n");
|
||||
}
|
||||
|
||||
prune:
|
||||
// then prune the allowlist
|
||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||
out:
|
||||
// free uid_list
|
||||
list_for_each_entry_safe (np, n, &uid_list, list) {
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_init()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_exit()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef __KSU_H_THRONE_TRACKER
|
||||
#define __KSU_H_THRONE_TRACKER
|
||||
|
||||
void ksu_throne_tracker_init();
|
||||
|
||||
void ksu_throne_tracker_exit();
|
||||
|
||||
void ksu_track_throne();
|
||||
|
||||
#endif
|
||||
@@ -23,7 +23,7 @@ int cam_io_w(uint32_t data, void __iomem *addr)
|
||||
return -EINVAL;
|
||||
|
||||
CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data);
|
||||
writel_relaxed(data, addr);
|
||||
writel_relaxed_no_log(data, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ int cam_io_w_mb(uint32_t data, void __iomem *addr)
|
||||
CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data);
|
||||
/* Ensure previous writes are done */
|
||||
wmb();
|
||||
writel_relaxed(data, addr);
|
||||
writel_relaxed_no_log(data, addr);
|
||||
/* Ensure previous writes are done */
|
||||
wmb();
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ int cam_io_w(uint32_t data, void __iomem *addr)
|
||||
return -EINVAL;
|
||||
|
||||
CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data);
|
||||
writel_relaxed(data, addr);
|
||||
writel_relaxed_no_log(data, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ int cam_io_w_mb(uint32_t data, void __iomem *addr)
|
||||
CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data);
|
||||
/* Ensure previous writes are done */
|
||||
wmb();
|
||||
writel_relaxed(data, addr);
|
||||
writel_relaxed_no_log(data, addr);
|
||||
/* Ensure previous writes are done */
|
||||
wmb();
|
||||
|
||||
|
||||
@@ -68,12 +68,12 @@
|
||||
do { \
|
||||
SDEROT_DBG("SDEREG.W:[%s:0x%X] <= 0x%X\n", #off, (off),\
|
||||
(u32)(data));\
|
||||
writel_relaxed( \
|
||||
writel_relaxed_no_log( \
|
||||
(REGDMA_OP_REGWRITE | \
|
||||
((off) & REGDMA_ADDR_OFFSET_MASK)), \
|
||||
p); \
|
||||
p += sizeof(u32); \
|
||||
writel_relaxed(data, p); \
|
||||
writel_relaxed_no_log(data, p); \
|
||||
p += sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
@@ -81,14 +81,14 @@
|
||||
do { \
|
||||
SDEROT_DBG("SDEREG.M:[%s:0x%X] <= 0x%X\n", #off, (off),\
|
||||
(u32)(data));\
|
||||
writel_relaxed( \
|
||||
writel_relaxed_no_log( \
|
||||
(REGDMA_OP_REGMODIFY | \
|
||||
((off) & REGDMA_ADDR_OFFSET_MASK)), \
|
||||
p); \
|
||||
p += sizeof(u32); \
|
||||
writel_relaxed(mask, p); \
|
||||
writel_relaxed_no_log(mask, p); \
|
||||
p += sizeof(u32); \
|
||||
writel_relaxed(data, p); \
|
||||
writel_relaxed_no_log(data, p); \
|
||||
p += sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
@@ -96,25 +96,25 @@
|
||||
do { \
|
||||
SDEROT_DBG("SDEREG.B:[%s:0x%X:0x%X]\n", #off, (off),\
|
||||
(u32)(len));\
|
||||
writel_relaxed( \
|
||||
writel_relaxed_no_log( \
|
||||
(REGDMA_OP_BLKWRITE_INC | \
|
||||
((off) & REGDMA_ADDR_OFFSET_MASK)), \
|
||||
p); \
|
||||
p += sizeof(u32); \
|
||||
writel_relaxed(len, p); \
|
||||
writel_relaxed_no_log(len, p); \
|
||||
p += sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
#define SDE_REGDMA_BLKWRITE_DATA(p, data) \
|
||||
do { \
|
||||
SDEROT_DBG("SDEREG.I:[:] <= 0x%X\n", (u32)(data));\
|
||||
writel_relaxed(data, p); \
|
||||
writel_relaxed_no_log(data, p); \
|
||||
p += sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
#define SDE_REGDMA_READ(p, data) \
|
||||
do { \
|
||||
data = readl_relaxed(p); \
|
||||
data = readl_relaxed_no_log(p); \
|
||||
p += sizeof(u32); \
|
||||
} while (0)
|
||||
|
||||
@@ -2041,7 +2041,7 @@ static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
|
||||
/* Write all command stream to Rotator blocks */
|
||||
/* Rotator will start right away after command stream finish writing */
|
||||
while (mem_rdptr < wrptr) {
|
||||
u32 op = REGDMA_OP_MASK & readl_relaxed(mem_rdptr);
|
||||
u32 op = REGDMA_OP_MASK & readl_relaxed_no_log(mem_rdptr);
|
||||
|
||||
switch (op) {
|
||||
case REGDMA_OP_NOP:
|
||||
|
||||
@@ -531,7 +531,7 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
|
||||
* based formats have frame descriptors.
|
||||
*/
|
||||
while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
|
||||
while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
|
||||
buffer[2] == ftype) {
|
||||
frame = &format->frame[format->nframes];
|
||||
if (ftype != UVC_VS_FRAME_FRAME_BASED)
|
||||
|
||||
@@ -1167,13 +1167,6 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
|
||||
|
||||
memset(it, 0, sizeof(*it));
|
||||
|
||||
/*
|
||||
* one of cell_count or cells_name must be provided to determine the
|
||||
* argument length.
|
||||
*/
|
||||
if (cell_count < 0 && !cells_name)
|
||||
return -EINVAL;
|
||||
|
||||
list = of_get_property(np, list_name, &size);
|
||||
if (!list)
|
||||
return -ENOENT;
|
||||
@@ -1223,20 +1216,11 @@ int of_phandle_iterator_next(struct of_phandle_iterator *it)
|
||||
|
||||
if (of_property_read_u32(it->node, it->cells_name,
|
||||
&count)) {
|
||||
/*
|
||||
* If both cell_count and cells_name is given,
|
||||
* fall back to cell_count in absence
|
||||
* of the cells_name property
|
||||
*/
|
||||
if (it->cell_count >= 0) {
|
||||
count = it->cell_count;
|
||||
} else {
|
||||
pr_err("%pOF: could not get %s for %pOF\n",
|
||||
it->parent,
|
||||
it->cells_name,
|
||||
it->node);
|
||||
goto err;
|
||||
}
|
||||
pr_err("%pOF: could not get %s for %pOF\n",
|
||||
it->parent,
|
||||
it->cells_name,
|
||||
it->node);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
count = it->cell_count;
|
||||
@@ -1399,17 +1383,10 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na
|
||||
const char *cells_name, int index,
|
||||
struct of_phandle_args *out_args)
|
||||
{
|
||||
int cell_count = -1;
|
||||
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* If cells_name is NULL we assume a cell count of 0 */
|
||||
if (!cells_name)
|
||||
cell_count = 0;
|
||||
|
||||
return __of_parse_phandle_with_args(np, list_name, cells_name,
|
||||
cell_count, index, out_args);
|
||||
return __of_parse_phandle_with_args(np, list_name, cells_name, 0,
|
||||
index, out_args);
|
||||
}
|
||||
EXPORT_SYMBOL(of_parse_phandle_with_args);
|
||||
|
||||
@@ -1475,24 +1452,7 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na
|
||||
struct of_phandle_iterator it;
|
||||
int rc, cur_index = 0;
|
||||
|
||||
/*
|
||||
* If cells_name is NULL we assume a cell count of 0. This makes
|
||||
* counting the phandles trivial as each 32bit word in the list is a
|
||||
* phandle and no arguments are to consider. So we don't iterate through
|
||||
* the list but just use the length to determine the phandle count.
|
||||
*/
|
||||
if (!cells_name) {
|
||||
const __be32 *list;
|
||||
int size;
|
||||
|
||||
list = of_get_property(np, list_name, &size);
|
||||
if (!list)
|
||||
return -ENOENT;
|
||||
|
||||
return size / sizeof(*list);
|
||||
}
|
||||
|
||||
rc = of_phandle_iterator_init(&it, np, list_name, cells_name, -1);
|
||||
rc = of_phandle_iterator_init(&it, np, list_name, cells_name, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ static int geni_se_iommu_map_and_attach(struct geni_se_device *geni_se_dev);
|
||||
*/
|
||||
unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
|
||||
{
|
||||
return readl_relaxed(base + offset);
|
||||
return readl_relaxed_no_log(base + offset);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_read_reg_nolog);
|
||||
|
||||
@@ -171,7 +171,7 @@ EXPORT_SYMBOL(geni_read_reg_nolog);
|
||||
*/
|
||||
void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
|
||||
{
|
||||
return writel_relaxed(value, (base + offset));
|
||||
return writel_relaxed_no_log(value, (base + offset));
|
||||
}
|
||||
EXPORT_SYMBOL(geni_write_reg_nolog);
|
||||
|
||||
|
||||
@@ -244,6 +244,40 @@ static const struct file_operations ufs_qcom_dbg_dbg_regs_desc = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int ufs_qcom_dbg_pm_qos_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(host->hba->host->host_lock, flags);
|
||||
|
||||
seq_printf(file, "enabled: %d\n", host->pm_qos.is_enabled);
|
||||
for (i = 0; i < host->pm_qos.num_groups && host->pm_qos.groups; i++)
|
||||
seq_printf(file,
|
||||
"CPU Group #%d(mask=0x%lx): active_reqs=%d, state=%d, latency=%d\n",
|
||||
i, host->pm_qos.groups[i].mask.bits[0],
|
||||
host->pm_qos.groups[i].active_reqs,
|
||||
host->pm_qos.groups[i].state,
|
||||
host->pm_qos.groups[i].latency_us);
|
||||
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ufs_qcom_dbg_pm_qos_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
return single_open(file, ufs_qcom_dbg_pm_qos_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ufs_qcom_dbg_pm_qos_desc = {
|
||||
.open = ufs_qcom_dbg_pm_qos_open,
|
||||
.read = seq_read,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root)
|
||||
{
|
||||
struct ufs_qcom_host *host;
|
||||
@@ -332,6 +366,17 @@ void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root)
|
||||
goto err;
|
||||
}
|
||||
|
||||
host->debugfs_files.pm_qos =
|
||||
debugfs_create_file("pm_qos", 0400,
|
||||
host->debugfs_files.debugfs_root, host,
|
||||
&ufs_qcom_dbg_pm_qos_desc);
|
||||
if (!host->debugfs_files.dbg_regs) {
|
||||
dev_err(host->hba->dev,
|
||||
"%s: failed create dbg_regs debugfs entry\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#define MAX_PROP_SIZE 32
|
||||
#define VDDP_REF_CLK_MIN_UV 1200000
|
||||
#define VDDP_REF_CLK_MAX_UV 1200000
|
||||
/* TODO: further tuning for this parameter may be required */
|
||||
#define UFS_QCOM_PM_QOS_UNVOTE_TIMEOUT_US (10000) /* microseconds */
|
||||
|
||||
#define UFS_QCOM_DEFAULT_DBG_PRINT_EN \
|
||||
(UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
||||
@@ -62,6 +64,7 @@ static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
|
||||
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||
u32 clk_1us_cycles,
|
||||
u32 clk_40ns_cycles);
|
||||
static void ufs_qcom_pm_qos_suspend(struct ufs_qcom_host *host);
|
||||
|
||||
static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len,
|
||||
char *prefix)
|
||||
@@ -844,6 +847,8 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* Unvote PM QoS */
|
||||
ufs_qcom_pm_qos_suspend(host);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -1475,6 +1480,7 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba)
|
||||
if (!host->disable_lpm) {
|
||||
hba->caps |= UFSHCD_CAP_CLK_GATING;
|
||||
hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
|
||||
hba->caps |= UFSHCD_CAP_CLK_SCALING;
|
||||
}
|
||||
hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
|
||||
|
||||
@@ -1552,6 +1558,395 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP /* CONFIG_SMP */
|
||||
static int ufs_qcom_cpu_to_group(struct ufs_qcom_host *host, int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cpu >= 0 && cpu < num_possible_cpus())
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++)
|
||||
if (cpumask_test_cpu(cpu, &host->pm_qos.groups[i].mask))
|
||||
return i;
|
||||
|
||||
return host->pm_qos.default_cpu;
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_req_start(struct ufs_hba *hba, struct request *req)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ufs_qcom_host *host;
|
||||
struct ufs_qcom_pm_qos_cpu_group *group;
|
||||
|
||||
if (!hba || !req)
|
||||
return;
|
||||
|
||||
host = ufshcd_get_variant(hba);
|
||||
if (!host->pm_qos.groups)
|
||||
return;
|
||||
|
||||
group = &host->pm_qos.groups[ufs_qcom_cpu_to_group(host, req->cpu)];
|
||||
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
if (!host->pm_qos.is_enabled)
|
||||
goto out;
|
||||
|
||||
group->active_reqs++;
|
||||
if (group->state != PM_QOS_REQ_VOTE &&
|
||||
group->state != PM_QOS_VOTED) {
|
||||
group->state = PM_QOS_REQ_VOTE;
|
||||
queue_work(host->pm_qos.workq, &group->vote_work);
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
}
|
||||
|
||||
/* hba->host->host_lock is assumed to be held by caller */
|
||||
static void __ufs_qcom_pm_qos_req_end(struct ufs_qcom_host *host, int req_cpu)
|
||||
{
|
||||
struct ufs_qcom_pm_qos_cpu_group *group;
|
||||
|
||||
if (!host->pm_qos.groups || !host->pm_qos.is_enabled)
|
||||
return;
|
||||
|
||||
group = &host->pm_qos.groups[ufs_qcom_cpu_to_group(host, req_cpu)];
|
||||
|
||||
if (--group->active_reqs)
|
||||
return;
|
||||
group->state = PM_QOS_REQ_UNVOTE;
|
||||
queue_work(host->pm_qos.workq, &group->unvote_work);
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_req_end(struct ufs_hba *hba, struct request *req,
|
||||
bool should_lock)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (!hba || !req)
|
||||
return;
|
||||
|
||||
if (should_lock)
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
__ufs_qcom_pm_qos_req_end(ufshcd_get_variant(hba), req->cpu);
|
||||
if (should_lock)
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_vote_work(struct work_struct *work)
|
||||
{
|
||||
struct ufs_qcom_pm_qos_cpu_group *group =
|
||||
container_of(work, struct ufs_qcom_pm_qos_cpu_group, vote_work);
|
||||
struct ufs_qcom_host *host = group->host;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(host->hba->host->host_lock, flags);
|
||||
|
||||
if (!host->pm_qos.is_enabled || !group->active_reqs) {
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
group->state = PM_QOS_VOTED;
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
|
||||
pm_qos_update_request(&group->req, group->latency_us);
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_unvote_work(struct work_struct *work)
|
||||
{
|
||||
struct ufs_qcom_pm_qos_cpu_group *group = container_of(work,
|
||||
struct ufs_qcom_pm_qos_cpu_group, unvote_work);
|
||||
struct ufs_qcom_host *host = group->host;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Check if new requests were submitted in the meantime and do not
|
||||
* unvote if so.
|
||||
*/
|
||||
spin_lock_irqsave(host->hba->host->host_lock, flags);
|
||||
|
||||
if (!host->pm_qos.is_enabled || group->active_reqs) {
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
group->state = PM_QOS_UNVOTED;
|
||||
spin_unlock_irqrestore(host->hba->host->host_lock, flags);
|
||||
|
||||
pm_qos_update_request_timeout(&group->req,
|
||||
group->latency_us, UFS_QCOM_PM_QOS_UNVOTE_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static ssize_t ufs_qcom_pm_qos_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", host->pm_qos.is_enabled);
|
||||
}
|
||||
|
||||
static ssize_t ufs_qcom_pm_qos_enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
unsigned long value;
|
||||
unsigned long flags;
|
||||
bool enable;
|
||||
int i;
|
||||
|
||||
if (kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
enable = !!value;
|
||||
|
||||
/*
|
||||
* Must take the spinlock and save irqs before changing the enabled
|
||||
* flag in order to keep correctness of PM QoS release.
|
||||
*/
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
if (enable == host->pm_qos.is_enabled) {
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
return count;
|
||||
}
|
||||
host->pm_qos.is_enabled = enable;
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
|
||||
if (!enable)
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||
cancel_work_sync(&host->pm_qos.groups[i].vote_work);
|
||||
cancel_work_sync(&host->pm_qos.groups[i].unvote_work);
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
host->pm_qos.groups[i].state = PM_QOS_UNVOTED;
|
||||
host->pm_qos.groups[i].active_reqs = 0;
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
pm_qos_update_request(&host->pm_qos.groups[i].req,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ufs_qcom_pm_qos_latency_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
int ret;
|
||||
int i;
|
||||
int offset = 0;
|
||||
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||
ret = snprintf(&buf[offset], PAGE_SIZE,
|
||||
"cpu group #%d(mask=0x%lx): %d\n", i,
|
||||
host->pm_qos.groups[i].mask.bits[0],
|
||||
host->pm_qos.groups[i].latency_us);
|
||||
if (ret > 0)
|
||||
offset += ret;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ssize_t ufs_qcom_pm_qos_latency_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||
unsigned long value;
|
||||
unsigned long flags;
|
||||
char *strbuf;
|
||||
char *strbuf_copy;
|
||||
char *token;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* reserve one byte for null termination */
|
||||
strbuf = kmalloc(count + 1, GFP_KERNEL);
|
||||
if (!strbuf)
|
||||
return -ENOMEM;
|
||||
strbuf_copy = strbuf;
|
||||
strlcpy(strbuf, buf, count + 1);
|
||||
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||
token = strsep(&strbuf, ",");
|
||||
if (!token)
|
||||
break;
|
||||
|
||||
ret = kstrtoul(token, 0, &value);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
host->pm_qos.groups[i].latency_us = value;
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
}
|
||||
|
||||
kfree(strbuf_copy);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int ufs_qcom_pm_qos_init(struct ufs_qcom_host *host)
|
||||
{
|
||||
struct device_node *node = host->hba->dev->of_node;
|
||||
struct device_attribute *attr;
|
||||
int ret = 0;
|
||||
int num_groups;
|
||||
int num_values;
|
||||
char wq_name[sizeof("ufs_pm_qos_00")];
|
||||
int i;
|
||||
|
||||
num_groups = of_property_count_u32_elems(node,
|
||||
"qcom,pm-qos-cpu-groups");
|
||||
if (num_groups <= 0)
|
||||
goto no_pm_qos;
|
||||
|
||||
num_values = of_property_count_u32_elems(node,
|
||||
"qcom,pm-qos-cpu-group-latency-us");
|
||||
if (num_values <= 0)
|
||||
goto no_pm_qos;
|
||||
|
||||
if (num_values != num_groups || num_groups > num_possible_cpus()) {
|
||||
dev_err(host->hba->dev, "%s: invalid count: num_groups=%d, num_values=%d, num_possible_cpus=%d\n",
|
||||
__func__, num_groups, num_values, num_possible_cpus());
|
||||
goto no_pm_qos;
|
||||
}
|
||||
|
||||
host->pm_qos.num_groups = num_groups;
|
||||
host->pm_qos.groups = kcalloc(host->pm_qos.num_groups,
|
||||
sizeof(struct ufs_qcom_pm_qos_cpu_group), GFP_KERNEL);
|
||||
if (!host->pm_qos.groups)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||
u32 mask;
|
||||
|
||||
ret = of_property_read_u32_index(node, "qcom,pm-qos-cpu-groups",
|
||||
i, &mask);
|
||||
if (ret)
|
||||
goto free_groups;
|
||||
host->pm_qos.groups[i].mask.bits[0] = mask;
|
||||
if (!cpumask_subset(&host->pm_qos.groups[i].mask,
|
||||
cpu_possible_mask)) {
|
||||
dev_err(host->hba->dev, "%s: invalid mask 0x%x for cpu group\n",
|
||||
__func__, mask);
|
||||
goto free_groups;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(node,
|
||||
"qcom,pm-qos-cpu-group-latency-us", i,
|
||||
&host->pm_qos.groups[i].latency_us);
|
||||
if (ret)
|
||||
goto free_groups;
|
||||
|
||||
host->pm_qos.groups[i].req.type = PM_QOS_REQ_AFFINE_CORES;
|
||||
host->pm_qos.groups[i].req.cpus_affine =
|
||||
host->pm_qos.groups[i].mask;
|
||||
host->pm_qos.groups[i].state = PM_QOS_UNVOTED;
|
||||
host->pm_qos.groups[i].active_reqs = 0;
|
||||
host->pm_qos.groups[i].host = host;
|
||||
|
||||
INIT_WORK(&host->pm_qos.groups[i].vote_work,
|
||||
ufs_qcom_pm_qos_vote_work);
|
||||
INIT_WORK(&host->pm_qos.groups[i].unvote_work,
|
||||
ufs_qcom_pm_qos_unvote_work);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,pm-qos-default-cpu",
|
||||
&host->pm_qos.default_cpu);
|
||||
if (ret || host->pm_qos.default_cpu > num_possible_cpus())
|
||||
host->pm_qos.default_cpu = 0;
|
||||
|
||||
/*
|
||||
* Use a single-threaded workqueue to assure work submitted to the queue
|
||||
* is performed in order. Consider the following 2 possible cases:
|
||||
*
|
||||
* 1. A new request arrives and voting work is scheduled for it. Before
|
||||
* the voting work is performed the request is finished and unvote
|
||||
* work is also scheduled.
|
||||
* 2. A request is finished and unvote work is scheduled. Before the
|
||||
* work is performed a new request arrives and voting work is also
|
||||
* scheduled.
|
||||
*
|
||||
* In both cases a vote work and unvote work wait to be performed.
|
||||
* If ordering is not guaranteed, then the end state might be the
|
||||
* opposite of the desired state.
|
||||
*/
|
||||
snprintf(wq_name, ARRAY_SIZE(wq_name), "%s_%d", "ufs_pm_qos",
|
||||
host->hba->host->host_no);
|
||||
host->pm_qos.workq = create_singlethread_workqueue(wq_name);
|
||||
if (!host->pm_qos.workq) {
|
||||
dev_err(host->hba->dev, "%s: failed to create the workqueue\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto free_groups;
|
||||
}
|
||||
|
||||
/* Initialization was ok, add all PM QoS requests */
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++)
|
||||
pm_qos_add_request(&host->pm_qos.groups[i].req,
|
||||
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
/* PM QoS latency sys-fs attribute */
|
||||
attr = &host->pm_qos.latency_attr;
|
||||
attr->show = ufs_qcom_pm_qos_latency_show;
|
||||
attr->store = ufs_qcom_pm_qos_latency_store;
|
||||
sysfs_attr_init(&attr->attr);
|
||||
attr->attr.name = "pm_qos_latency_us";
|
||||
attr->attr.mode = 0644;
|
||||
if (device_create_file(host->hba->var->dev, attr))
|
||||
dev_dbg(host->hba->dev, "Failed to create sysfs for pm_qos_latency_us\n");
|
||||
|
||||
/* PM QoS enable sys-fs attribute */
|
||||
attr = &host->pm_qos.enable_attr;
|
||||
attr->show = ufs_qcom_pm_qos_enable_show;
|
||||
attr->store = ufs_qcom_pm_qos_enable_store;
|
||||
sysfs_attr_init(&attr->attr);
|
||||
attr->attr.name = "pm_qos_enable";
|
||||
attr->attr.mode = 0644;
|
||||
if (device_create_file(host->hba->var->dev, attr))
|
||||
dev_dbg(host->hba->dev, "Failed to create sysfs for pm_qos enable\n");
|
||||
|
||||
host->pm_qos.is_enabled = true;
|
||||
|
||||
return 0;
|
||||
|
||||
free_groups:
|
||||
kfree(host->pm_qos.groups);
|
||||
no_pm_qos:
|
||||
host->pm_qos.groups = NULL;
|
||||
return ret ? ret : -ENOTSUPP;
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_suspend(struct ufs_qcom_host *host)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!host->pm_qos.groups)
|
||||
return;
|
||||
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++)
|
||||
flush_work(&host->pm_qos.groups[i].unvote_work);
|
||||
}
|
||||
|
||||
static void ufs_qcom_pm_qos_remove(struct ufs_qcom_host *host)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!host->pm_qos.groups)
|
||||
return;
|
||||
|
||||
for (i = 0; i < host->pm_qos.num_groups; i++)
|
||||
pm_qos_remove_request(&host->pm_qos.groups[i].req);
|
||||
destroy_workqueue(host->pm_qos.workq);
|
||||
|
||||
kfree(host->pm_qos.groups);
|
||||
host->pm_qos.groups = NULL;
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#define ANDROID_BOOT_DEV_MAX 30
|
||||
static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
|
||||
|
||||
@@ -1714,6 +2109,10 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
||||
goto out_variant_clear;
|
||||
}
|
||||
|
||||
err = ufs_qcom_pm_qos_init(host);
|
||||
if (err)
|
||||
dev_info(dev, "%s: PM QoS will be disabled\n", __func__);
|
||||
|
||||
/* restore the secure configuration */
|
||||
ufs_qcom_update_sec_cfg(hba, true);
|
||||
|
||||
@@ -1842,6 +2241,7 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
|
||||
host->is_phy_pwr_on = false;
|
||||
}
|
||||
phy_exit(host->generic_phy);
|
||||
ufs_qcom_pm_qos_remove(host);
|
||||
}
|
||||
|
||||
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||
@@ -2308,9 +2708,15 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ufs_hba_pm_qos_variant_ops ufs_hba_pm_qos_variant_ops = {
|
||||
.req_start = ufs_qcom_pm_qos_req_start,
|
||||
.req_end = ufs_qcom_pm_qos_req_end,
|
||||
};
|
||||
|
||||
static struct ufs_hba_variant ufs_hba_qcom_variant = {
|
||||
.name = "qcom",
|
||||
.vops = &ufs_hba_qcom_vops,
|
||||
.pm_qos_vops = &ufs_hba_pm_qos_variant_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define UFS_QCOM_H_
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include "ufshcd.h"
|
||||
|
||||
#define MAX_UFS_QCOM_HOSTS 2
|
||||
@@ -244,9 +245,62 @@ struct qcom_debugfs_files {
|
||||
struct dentry *testbus_cfg;
|
||||
struct dentry *testbus_bus;
|
||||
struct dentry *dbg_regs;
|
||||
struct dentry *pm_qos;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* PM QoS voting state */
|
||||
enum ufs_qcom_pm_qos_state {
|
||||
PM_QOS_UNVOTED,
|
||||
PM_QOS_VOTED,
|
||||
PM_QOS_REQ_VOTE,
|
||||
PM_QOS_REQ_UNVOTE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_qcom_pm_qos_cpu_group - data related to cluster PM QoS voting
|
||||
* logic
|
||||
* @req: request object for PM QoS
|
||||
* @vote_work: work object for voting procedure
|
||||
* @unvote_work: work object for un-voting procedure
|
||||
* @host: back pointer to the main structure
|
||||
* @state: voting state machine current state
|
||||
* @latency_us: requested latency value used for cluster voting, in
|
||||
* microseconds
|
||||
* @mask: cpu mask defined for this cluster
|
||||
* @active_reqs: number of active requests on this cluster
|
||||
*/
|
||||
struct ufs_qcom_pm_qos_cpu_group {
|
||||
struct pm_qos_request req;
|
||||
struct work_struct vote_work;
|
||||
struct work_struct unvote_work;
|
||||
struct ufs_qcom_host *host;
|
||||
enum ufs_qcom_pm_qos_state state;
|
||||
s32 latency_us;
|
||||
cpumask_t mask;
|
||||
int active_reqs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_qcom_pm_qos - data related to PM QoS voting logic
|
||||
* @groups: PM QoS cpu group state array
|
||||
* @enable_attr: sysfs attribute to enable/disable PM QoS voting logic
|
||||
* @latency_attr: sysfs attribute to set latency value
|
||||
* @workq: single threaded workqueue to run PM QoS voting/unvoting
|
||||
* @num_clusters: number of clusters defined
|
||||
* @default_cpu: cpu to use for voting for request not specifying a cpu
|
||||
* @is_enabled: flag specifying whether voting logic is enabled
|
||||
*/
|
||||
struct ufs_qcom_pm_qos {
|
||||
struct ufs_qcom_pm_qos_cpu_group *groups;
|
||||
struct device_attribute enable_attr;
|
||||
struct device_attribute latency_attr;
|
||||
struct workqueue_struct *workq;
|
||||
int num_groups;
|
||||
int default_cpu;
|
||||
bool is_enabled;
|
||||
};
|
||||
|
||||
struct ufs_qcom_host {
|
||||
/*
|
||||
* Set this capability if host controller supports the QUniPro mode
|
||||
@@ -283,6 +337,9 @@ struct ufs_qcom_host {
|
||||
struct clk *rx_l1_sync_clk;
|
||||
struct clk *tx_l1_sync_clk;
|
||||
|
||||
/* PM Quality-of-Service (QoS) data */
|
||||
struct ufs_qcom_pm_qos pm_qos;
|
||||
|
||||
bool disable_lpm;
|
||||
bool is_lane_clks_enabled;
|
||||
bool sec_cfg_updated;
|
||||
|
||||
@@ -1220,6 +1220,22 @@ static void ufshcd_cmd_log_init(struct ufs_hba *hba)
|
||||
{
|
||||
}
|
||||
|
||||
static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type,
|
||||
unsigned int tag, u8 cmd_id, u8 idn, u8 lun,
|
||||
sector_t lba, int transfer_len)
|
||||
{
|
||||
struct ufshcd_cmd_log_entry entry;
|
||||
|
||||
entry.str = str;
|
||||
entry.lba = lba;
|
||||
entry.cmd_id = cmd_id;
|
||||
entry.transfer_len = transfer_len;
|
||||
entry.doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
|
||||
entry.tag = tag;
|
||||
|
||||
ufshcd_add_command_trace(hba, &entry);
|
||||
}
|
||||
|
||||
static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
|
||||
{
|
||||
}
|
||||
@@ -3495,19 +3511,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
|
||||
static inline
|
||||
int ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
|
||||
{
|
||||
if (hba->lrb[task_tag].cmd) {
|
||||
u8 opcode = (u8)(*hba->lrb[task_tag].cmd->cmnd);
|
||||
|
||||
if (opcode == SECURITY_PROTOCOL_OUT && hba->security_in) {
|
||||
hba->security_in--;
|
||||
} else if (opcode == SECURITY_PROTOCOL_IN) {
|
||||
if (hba->security_in) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
hba->security_in++;
|
||||
}
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
hba->lrb[task_tag].issue_time_stamp = ktime_get();
|
||||
hba->lrb[task_tag].complete_time_stamp = ktime_set(0, 0);
|
||||
@@ -3519,7 +3523,7 @@ int ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
|
||||
ufshcd_cond_add_cmd_trace(hba, task_tag,
|
||||
hba->lrb[task_tag].cmd ? "scsi_send" : "dev_cmd_send");
|
||||
ufshcd_update_tag_stats(hba, task_tag);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4217,48 +4221,6 @@ static inline void ufshcd_put_read_lock(struct ufs_hba *hba)
|
||||
up_read(&hba->lock);
|
||||
}
|
||||
|
||||
static void ufshcd_pm_qos_get_worker(struct work_struct *work)
|
||||
{
|
||||
struct ufs_hba *hba = container_of(work, typeof(*hba), pm_qos.get_work);
|
||||
|
||||
if (!atomic_read(&hba->pm_qos.count))
|
||||
return;
|
||||
|
||||
mutex_lock(&hba->pm_qos.lock);
|
||||
if (atomic_read(&hba->pm_qos.count) && !hba->pm_qos.active) {
|
||||
pm_qos_update_request(&hba->pm_qos.req, 100);
|
||||
hba->pm_qos.active = true;
|
||||
}
|
||||
mutex_unlock(&hba->pm_qos.lock);
|
||||
}
|
||||
|
||||
static void ufshcd_pm_qos_put_worker(struct work_struct *work)
|
||||
{
|
||||
struct ufs_hba *hba = container_of(work, typeof(*hba), pm_qos.put_work);
|
||||
|
||||
if (atomic_read(&hba->pm_qos.count))
|
||||
return;
|
||||
|
||||
mutex_lock(&hba->pm_qos.lock);
|
||||
if (!atomic_read(&hba->pm_qos.count) && hba->pm_qos.active) {
|
||||
pm_qos_update_request(&hba->pm_qos.req, PM_QOS_DEFAULT_VALUE);
|
||||
hba->pm_qos.active = false;
|
||||
}
|
||||
mutex_unlock(&hba->pm_qos.lock);
|
||||
}
|
||||
|
||||
static void ufshcd_pm_qos_get(struct ufs_hba *hba)
|
||||
{
|
||||
if (atomic_inc_return(&hba->pm_qos.count) == 1)
|
||||
queue_work(system_unbound_wq, &hba->pm_qos.get_work);
|
||||
}
|
||||
|
||||
static void ufshcd_pm_qos_put(struct ufs_hba *hba)
|
||||
{
|
||||
if (atomic_dec_return(&hba->pm_qos.count) == 0)
|
||||
queue_work(system_unbound_wq, &hba->pm_qos.put_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_queuecommand - main entry point for SCSI requests
|
||||
* @cmd: command from SCSI Midlayer
|
||||
@@ -4274,16 +4236,12 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
int tag;
|
||||
int err = 0;
|
||||
bool has_read_lock = false;
|
||||
bool cmd_sent = false;
|
||||
|
||||
hba = shost_priv(host);
|
||||
|
||||
if (!cmd || !cmd->request || !hba)
|
||||
return -EINVAL;
|
||||
|
||||
/* Wake the CPU managing the IRQ as soon as possible */
|
||||
ufshcd_pm_qos_get(hba);
|
||||
|
||||
tag = cmd->request->tag;
|
||||
if (!ufshcd_valid_tag(hba, tag)) {
|
||||
dev_err(hba->dev,
|
||||
@@ -4295,13 +4253,10 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
err = ufshcd_get_read_lock(hba, cmd->device->lun);
|
||||
if (unlikely(err < 0)) {
|
||||
if (err == -EPERM) {
|
||||
err = SCSI_MLQUEUE_HOST_BUSY;
|
||||
goto out_pm_qos;
|
||||
}
|
||||
if (err == -EAGAIN) {
|
||||
err = SCSI_MLQUEUE_HOST_BUSY;
|
||||
goto out_pm_qos;
|
||||
return SCSI_MLQUEUE_HOST_BUSY;
|
||||
}
|
||||
if (err == -EAGAIN)
|
||||
return SCSI_MLQUEUE_HOST_BUSY;
|
||||
} else if (err == 1) {
|
||||
has_read_lock = true;
|
||||
}
|
||||
@@ -4382,6 +4337,9 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
if (ufshcd_is_hibern8_on_idle_allowed(hba))
|
||||
WARN_ON(hba->hibern8_on_idle.state != HIBERN8_EXITED);
|
||||
|
||||
/* Vote PM QoS for the request */
|
||||
ufshcd_vops_pm_qos_req_start(hba, cmd->request);
|
||||
|
||||
/* IO svc time latency histogram */
|
||||
if (hba != NULL && cmd->request != NULL) {
|
||||
if (hba->latency_hist_enabled) {
|
||||
@@ -4426,6 +4384,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
lrbp->cmd = NULL;
|
||||
clear_bit_unlock(tag, &hba->lrb_in_use);
|
||||
ufshcd_release_all(hba);
|
||||
ufshcd_vops_pm_qos_req_end(hba, cmd->request, true);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -4435,6 +4394,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
lrbp->cmd = NULL;
|
||||
clear_bit_unlock(tag, &hba->lrb_in_use);
|
||||
ufshcd_release_all(hba);
|
||||
ufshcd_vops_pm_qos_req_end(hba, cmd->request, true);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -4452,29 +4412,18 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
||||
lrbp->cmd = NULL;
|
||||
clear_bit_unlock(tag, &hba->lrb_in_use);
|
||||
ufshcd_release_all(hba);
|
||||
ufshcd_vops_pm_qos_req_end(hba, cmd->request, true);
|
||||
dev_err(hba->dev, "%s: failed sending command, %d\n",
|
||||
__func__, err);
|
||||
if (err == -EINVAL) {
|
||||
set_host_byte(cmd, DID_ERROR);
|
||||
if (has_read_lock)
|
||||
ufshcd_put_read_lock(hba);
|
||||
cmd->scsi_done(cmd);
|
||||
err = 0;
|
||||
goto out_pm_qos;
|
||||
}
|
||||
err = DID_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd_sent = true;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
out:
|
||||
if (has_read_lock)
|
||||
ufshcd_put_read_lock(hba);
|
||||
out_pm_qos:
|
||||
if (!cmd_sent)
|
||||
ufshcd_pm_qos_put(hba);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -7532,11 +7481,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
|
||||
* this must be called before calling
|
||||
* ->scsi_done() callback.
|
||||
*/
|
||||
ufshcd_vops_pm_qos_req_end(hba, cmd->request,
|
||||
false);
|
||||
}
|
||||
|
||||
req = cmd->request;
|
||||
if (req) {
|
||||
ufshcd_pm_qos_put(hba);
|
||||
/* Update IO svc time latency histogram */
|
||||
if (req->lat_hist_enabled) {
|
||||
ktime_t completion;
|
||||
@@ -7607,8 +7557,15 @@ void ufshcd_abort_outstanding_transfer_requests(struct ufs_hba *hba, int result)
|
||||
/* Mark completed command as NULL in LRB */
|
||||
lrbp->cmd = NULL;
|
||||
ufshcd_release_all(hba);
|
||||
if (cmd->request)
|
||||
ufshcd_pm_qos_put(hba);
|
||||
if (cmd->request) {
|
||||
/*
|
||||
* As we are accessing the "request" structure,
|
||||
* this must be called before calling
|
||||
* ->scsi_done() callback.
|
||||
*/
|
||||
ufshcd_vops_pm_qos_req_end(hba, cmd->request,
|
||||
true);
|
||||
}
|
||||
/* Do not touch lrbp after scsi done */
|
||||
cmd->scsi_done(cmd);
|
||||
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) {
|
||||
@@ -12742,9 +12699,6 @@ void ufshcd_remove(struct ufs_hba *hba)
|
||||
/* disable interrupts */
|
||||
ufshcd_disable_intr(hba, hba->intr_mask);
|
||||
ufshcd_hba_stop(hba, true);
|
||||
cancel_work_sync(&hba->pm_qos.put_work);
|
||||
cancel_work_sync(&hba->pm_qos.get_work);
|
||||
pm_qos_remove_request(&hba->pm_qos.req);
|
||||
|
||||
ufshcd_exit_clk_gating(hba);
|
||||
ufshcd_exit_hibern8_on_idle(hba);
|
||||
@@ -13023,14 +12977,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
||||
*/
|
||||
ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
|
||||
|
||||
mutex_init(&hba->pm_qos.lock);
|
||||
INIT_WORK(&hba->pm_qos.get_work, ufshcd_pm_qos_get_worker);
|
||||
INIT_WORK(&hba->pm_qos.put_work, ufshcd_pm_qos_put_worker);
|
||||
hba->pm_qos.req.type = PM_QOS_REQ_AFFINE_IRQ;
|
||||
hba->pm_qos.req.irq = irq;
|
||||
pm_qos_add_request(&hba->pm_qos.req, PM_QOS_CPU_DMA_LATENCY,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
/* IRQ registration */
|
||||
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED,
|
||||
dev_name(dev), hba);
|
||||
@@ -13137,7 +13083,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
||||
out_remove_scsi_host:
|
||||
scsi_remove_host(hba->host);
|
||||
exit_gating:
|
||||
pm_qos_remove_request(&hba->pm_qos.req);
|
||||
ufshcd_exit_clk_gating(hba);
|
||||
ufshcd_exit_latency_hist(hba);
|
||||
out_disable:
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include "unipro.h"
|
||||
|
||||
#include <asm/irq.h>
|
||||
@@ -403,6 +402,14 @@ struct ufs_hba_variant_ops {
|
||||
const union ufs_crypto_cfg_entry *cfg, int slot);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_hba_pm_qos_variant_ops - variant specific PM QoS callbacks
|
||||
*/
|
||||
struct ufs_hba_pm_qos_variant_ops {
|
||||
void (*req_start)(struct ufs_hba *, struct request *);
|
||||
void (*req_end)(struct ufs_hba *, struct request *, bool);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_hba_variant - variant specific parameters
|
||||
* @name: variant name
|
||||
@@ -411,6 +418,7 @@ struct ufs_hba_variant {
|
||||
struct device *dev;
|
||||
const char *name;
|
||||
struct ufs_hba_variant_ops *vops;
|
||||
struct ufs_hba_pm_qos_variant_ops *pm_qos_vops;
|
||||
};
|
||||
|
||||
struct keyslot_mgmt_ll_ops;
|
||||
@@ -1104,8 +1112,6 @@ struct ufs_hba {
|
||||
/* Number of requests aborts */
|
||||
int req_abort_count;
|
||||
|
||||
u32 security_in;
|
||||
|
||||
/* Number of lanes available (1 or 2) for Rx/Tx */
|
||||
u32 lanes_per_direction;
|
||||
|
||||
@@ -1215,15 +1221,6 @@ struct ufs_hba {
|
||||
void *crypto_DO_NOT_USE[8];
|
||||
#endif /* CONFIG_SCSI_UFS_CRYPTO */
|
||||
|
||||
struct {
|
||||
struct pm_qos_request req;
|
||||
struct work_struct get_work;
|
||||
struct work_struct put_work;
|
||||
struct mutex lock;
|
||||
atomic_t count;
|
||||
bool active;
|
||||
} pm_qos;
|
||||
|
||||
#if IS_ENABLED(CONFIG_BLK_TURBO_WRITE)
|
||||
bool support_tw;
|
||||
bool tw_state_not_allowed;
|
||||
@@ -1697,6 +1694,21 @@ static inline void ufshcd_vops_remove_debugfs(struct ufs_hba *hba)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void ufshcd_vops_pm_qos_req_start(struct ufs_hba *hba,
|
||||
struct request *req)
|
||||
{
|
||||
if (hba->var && hba->var->pm_qos_vops &&
|
||||
hba->var->pm_qos_vops->req_start)
|
||||
hba->var->pm_qos_vops->req_start(hba, req);
|
||||
}
|
||||
|
||||
static inline void ufshcd_vops_pm_qos_req_end(struct ufs_hba *hba,
|
||||
struct request *req, bool lock)
|
||||
{
|
||||
if (hba->var && hba->var->pm_qos_vops && hba->var->pm_qos_vops->req_end)
|
||||
hba->var->pm_qos_vops->req_end(hba, req, lock);
|
||||
}
|
||||
|
||||
#define UFS_DEV_ATTR(name, fmt, args...) \
|
||||
static ssize_t ufs_##name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
|
||||
@@ -127,11 +127,11 @@ unsigned long long int msm_timer_get_sclk_ticks(void)
|
||||
if (!sclk_tick)
|
||||
return -EINVAL;
|
||||
while (loop_zero_count--) {
|
||||
t1 = __raw_readl(sclk_tick);
|
||||
t1 = __raw_readl_no_log(sclk_tick);
|
||||
do {
|
||||
udelay(1);
|
||||
t2 = t1;
|
||||
t1 = __raw_readl(sclk_tick);
|
||||
t1 = __raw_readl_no_log(sclk_tick);
|
||||
} while ((t2 != t1) && --loop_count);
|
||||
if (!loop_count) {
|
||||
pr_err("boot_stats: SCLK did not stabilize\n");
|
||||
|
||||
@@ -197,7 +197,7 @@ static void dcc_sram_memset(const struct device *dev, void __iomem *dst,
|
||||
}
|
||||
|
||||
while (count >= 4) {
|
||||
__raw_writel(qc, dst);
|
||||
__raw_writel_no_log(qc, dst);
|
||||
dst += 4;
|
||||
count -= 4;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ static int dcc_sram_memcpy(void *to, const void __iomem *from,
|
||||
}
|
||||
|
||||
while (count >= 4) {
|
||||
*(unsigned int *)to = __raw_readl(from);
|
||||
*(unsigned int *)to = __raw_readl_no_log(from);
|
||||
to += 4;
|
||||
from += 4;
|
||||
count -= 4;
|
||||
@@ -1929,7 +1929,7 @@ static int dcc_v2_restore(struct device *dev)
|
||||
data = drvdata->sram_save_state;
|
||||
|
||||
for (i = 0; i < drvdata->ram_size / 4; i++)
|
||||
__raw_writel(data[i],
|
||||
__raw_writel_no_log(data[i],
|
||||
drvdata->ram_base + (i * 4));
|
||||
|
||||
state = drvdata->reg_save_state;
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
/* spread out etm register write */
|
||||
#define etm_writel(etm, val, off) \
|
||||
do { \
|
||||
writel_relaxed(val, etm->base + off); \
|
||||
writel_relaxed_no_log(val, etm->base + off); \
|
||||
udelay(20); \
|
||||
} while (0)
|
||||
|
||||
@@ -194,13 +194,13 @@ do { \
|
||||
__raw_writel(val, etm->base + off)
|
||||
|
||||
#define etm_readl(etm, off) \
|
||||
readl_relaxed(etm->base + off)
|
||||
readl_relaxed_no_log(etm->base + off)
|
||||
|
||||
#define etm_writeq(etm, val, off) \
|
||||
writeq_relaxed(val, etm->base + off)
|
||||
writeq_relaxed_no_log(val, etm->base + off)
|
||||
|
||||
#define etm_readq(etm, off) \
|
||||
readq_relaxed(etm->base + off)
|
||||
readq_relaxed_no_log(etm->base + off)
|
||||
|
||||
#define ETM_LOCK(base) \
|
||||
do { \
|
||||
|
||||
@@ -155,7 +155,7 @@ static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
|
||||
sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr);
|
||||
trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr);
|
||||
|
||||
code = readl_relaxed(trdy);
|
||||
code = readl_relaxed_no_log(trdy);
|
||||
|
||||
if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >>
|
||||
TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT)) {
|
||||
@@ -170,7 +170,7 @@ static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
|
||||
/* Wait for 2.5 ms for tsens controller to recover */
|
||||
do {
|
||||
udelay(500);
|
||||
code = readl_relaxed(trdy);
|
||||
code = readl_relaxed_no_log(trdy);
|
||||
if (code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) {
|
||||
TSENS_DUMP(tmdev, "%s",
|
||||
"tsens controller recovered\n");
|
||||
@@ -296,7 +296,7 @@ sensor_read:
|
||||
|
||||
tmdev->trdy_fail_ctr = 0;
|
||||
|
||||
code = readl_relaxed(sensor_addr +
|
||||
code = readl_relaxed_no_log(sensor_addr +
|
||||
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
|
||||
last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
|
||||
|
||||
@@ -305,7 +305,7 @@ sensor_read:
|
||||
goto dbg;
|
||||
}
|
||||
|
||||
code = readl_relaxed(sensor_addr +
|
||||
code = readl_relaxed_no_log(sensor_addr +
|
||||
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
|
||||
last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK;
|
||||
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
|
||||
@@ -314,7 +314,7 @@ sensor_read:
|
||||
goto dbg;
|
||||
}
|
||||
|
||||
code = readl_relaxed(sensor_addr +
|
||||
code = readl_relaxed_no_log(sensor_addr +
|
||||
(sensor->hw_id <<
|
||||
TSENS_STATUS_ADDR_OFFSET));
|
||||
last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK;
|
||||
|
||||
@@ -14,8 +14,6 @@ obj-y := open.o read_write.o file_table.o super.o \
|
||||
pnode.o splice.o sync.o utimes.o \
|
||||
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
|
||||
|
||||
obj-$(CONFIG_KSU_SUSFS) += susfs.o
|
||||
|
||||
ifeq ($(CONFIG_BLOCK),y)
|
||||
obj-y += buffer.o block_dev.o direct-io.o mpage.o
|
||||
else
|
||||
|
||||
14
fs/dcache.c
14
fs/dcache.c
@@ -39,9 +39,6 @@
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/list_lru.h>
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
#include <linux/susfs_def.h>
|
||||
#endif
|
||||
|
||||
#include "internal.h"
|
||||
#include "mount.h"
|
||||
@@ -2233,11 +2230,6 @@ seqretry:
|
||||
continue;
|
||||
if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0)
|
||||
continue;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
*seqp = seq;
|
||||
return dentry;
|
||||
@@ -2321,12 +2313,6 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
|
||||
if (dentry->d_name.hash != hash)
|
||||
continue;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (dentry->d_parent != parent)
|
||||
goto next;
|
||||
|
||||
@@ -602,10 +602,6 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv)
|
||||
return dentry;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_KSU) && !defined(CONFIG_KPROBES)
|
||||
extern int ksu_handle_devpts(struct inode*);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* devpts_get_priv -- get private data for a slave
|
||||
* @pts_inode: inode of the slave
|
||||
@@ -614,10 +610,6 @@ extern int ksu_handle_devpts(struct inode*);
|
||||
*/
|
||||
void *devpts_get_priv(struct dentry *dentry)
|
||||
{
|
||||
#if defined(CONFIG_KSU) && !defined(CONFIG_KPROBES)
|
||||
ksu_handle_devpts(dentry->d_inode);
|
||||
#endif
|
||||
|
||||
if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC)
|
||||
return NULL;
|
||||
return dentry->d_fsdata;
|
||||
|
||||
29
fs/exec.c
29
fs/exec.c
@@ -1860,14 +1860,6 @@ static int exec_binprm(struct linux_binprm *bprm)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
extern bool ksu_execveat_hook __read_mostly;
|
||||
extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags);
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
@@ -1881,11 +1873,6 @@ static int __do_execve_file(int fd, struct filename *filename,
|
||||
struct files_struct *displaced;
|
||||
int retval;
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
if (unlikely(ksu_execveat_hook))
|
||||
ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
|
||||
#endif
|
||||
|
||||
if (IS_ERR(filename))
|
||||
return PTR_ERR(filename);
|
||||
|
||||
@@ -2148,23 +2135,11 @@ void set_dumpable(struct mm_struct *mm, int value)
|
||||
} while (cmpxchg(&mm->flags, old, new) != old);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
extern int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
void *__never_use_argv, void *__never_use_envp,
|
||||
int *__never_use_flags);
|
||||
int at_fdcwd = AT_FDCWD;
|
||||
#endif
|
||||
|
||||
SYSCALL_DEFINE3(execve,
|
||||
const char __user *, filename,
|
||||
const char __user *const __user *, argv,
|
||||
const char __user *const __user *, envp)
|
||||
{
|
||||
#ifdef CONFIG_KSU
|
||||
if (!ksu_execveat_hook)
|
||||
ksu_handle_execve_sucompat(&at_fdcwd, &filename, NULL, NULL, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKP_KDP
|
||||
struct filename *path = getname(filename);
|
||||
int error = PTR_ERR(path);
|
||||
@@ -2200,10 +2175,6 @@ COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename,
|
||||
const compat_uptr_t __user *, argv,
|
||||
const compat_uptr_t __user *, envp)
|
||||
{
|
||||
#ifdef CONFIG_KSU
|
||||
if (!ksu_execveat_hook)
|
||||
ksu_handle_execve_sucompat(&at_fdcwd, &filename, NULL, NULL, NULL); /* 32-bit su support */
|
||||
#endif
|
||||
return compat_do_execve(getname(filename), argv, envp);
|
||||
}
|
||||
|
||||
|
||||
130
fs/namei.c
130
fs/namei.c
@@ -39,9 +39,6 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/uaccess.h>
|
||||
#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT)
|
||||
#include <linux/susfs_def.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSCRYPT_SDP
|
||||
#include <linux/fscrypto_sdp_name.h>
|
||||
@@ -1034,12 +1031,6 @@ static inline int may_follow_link(struct nameidata *nd)
|
||||
const struct inode *parent;
|
||||
kuid_t puid;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (nd->inode && unlikely(nd->inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sysctl_protected_symlinks)
|
||||
return 0;
|
||||
|
||||
@@ -1116,12 +1107,6 @@ static int may_linkat(struct path *link)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (link->dentry->d_inode && unlikely(link->dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sysctl_protected_hardlinks)
|
||||
return 0;
|
||||
|
||||
@@ -1161,12 +1146,6 @@ static int may_linkat(struct path *link)
|
||||
static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid,
|
||||
struct inode * const inode)
|
||||
{
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
|
||||
(!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
|
||||
likely(!(dir_mode & S_ISVTX)) ||
|
||||
@@ -1707,12 +1686,6 @@ static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry,
|
||||
dput(dentry);
|
||||
dentry = old;
|
||||
}
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
dput(dentry);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif
|
||||
return dentry;
|
||||
}
|
||||
|
||||
@@ -1856,12 +1829,6 @@ again:
|
||||
dentry = old;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
dput(dentry);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
#endif
|
||||
out:
|
||||
inode_unlock_shared(inode);
|
||||
return dentry;
|
||||
@@ -2340,12 +2307,6 @@ OK:
|
||||
}
|
||||
return -ENOTDIR;
|
||||
}
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
// we deal with sus sub path here
|
||||
if (nd->inode && unlikely(nd->inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2551,12 +2512,6 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags,
|
||||
if (likely(!retval))
|
||||
audit_inode(name, path->dentry, flags & LOOKUP_PARENT);
|
||||
restore_nameidata();
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (!retval && path->dentry->d_inode && unlikely(path->dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
putname(name);
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
putname(name);
|
||||
return retval;
|
||||
}
|
||||
@@ -3014,12 +2969,6 @@ static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *vi
|
||||
if (IS_APPEND(dir))
|
||||
return -EPERM;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (check_sticky(dir, inode) || IS_APPEND(inode) ||
|
||||
IS_IMMUTABLE(inode) || IS_SWAPFILE(inode) || HAS_UNMAPPED_ID(inode))
|
||||
return -EPERM;
|
||||
@@ -3048,20 +2997,8 @@ static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *vi
|
||||
*/
|
||||
static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
|
||||
{
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
int error;
|
||||
#endif
|
||||
struct user_namespace *s_user_ns;
|
||||
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (child->d_inode && unlikely(child->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
if (child->d_inode)
|
||||
return -EEXIST;
|
||||
if (IS_DEADDIR(dir))
|
||||
@@ -3181,12 +3118,6 @@ static int may_open(const struct path *path, int acc_mode, int flag)
|
||||
if (!inode)
|
||||
return -ENOENT;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
case S_IFLNK:
|
||||
return -ELOOP;
|
||||
@@ -3258,20 +3189,7 @@ static inline int open_to_namei_flags(int flag)
|
||||
static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct user_namespace *s_user_ns;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
int error;
|
||||
|
||||
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
error = security_path_mknod(dir, dentry, mode, 0);
|
||||
#else
|
||||
int error = security_path_mknod(dir, dentry, mode, 0);
|
||||
#endif
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -3415,12 +3333,6 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
||||
}
|
||||
if (dentry->d_inode) {
|
||||
/* Cached positive dentry: will open in f_op->open */
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
dput(dentry);
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
goto out_no_open;
|
||||
}
|
||||
|
||||
@@ -3464,16 +3376,6 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
||||
mode, opened);
|
||||
if (unlikely(error == -ENOENT) && create_error)
|
||||
error = create_error;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
if (create_error) {
|
||||
dput(dentry);
|
||||
return create_error;
|
||||
}
|
||||
dput(dentry);
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -3489,12 +3391,6 @@ no_open:
|
||||
}
|
||||
dput(dentry);
|
||||
dentry = res;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
|
||||
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
|
||||
dput(dentry);
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3864,19 +3760,12 @@ out2:
|
||||
return file;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
|
||||
extern struct filename* susfs_get_redirected_path(unsigned long ino);
|
||||
#endif
|
||||
|
||||
struct file *do_filp_open(int dfd, struct filename *pathname,
|
||||
const struct open_flags *op)
|
||||
{
|
||||
struct nameidata nd;
|
||||
int flags = op->lookup_flags;
|
||||
struct file *filp;
|
||||
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
|
||||
struct filename *fake_pathname;
|
||||
#endif
|
||||
|
||||
set_nameidata(&nd, dfd, pathname);
|
||||
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
|
||||
@@ -3884,25 +3773,6 @@ struct file *do_filp_open(int dfd, struct filename *pathname,
|
||||
filp = path_openat(&nd, op, flags);
|
||||
if (unlikely(filp == ERR_PTR(-ESTALE)))
|
||||
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
|
||||
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
|
||||
if (!IS_ERR(filp) && unlikely(filp->f_inode->i_state & INODE_STATE_OPEN_REDIRECT) && current_uid().val < 2000) {
|
||||
fake_pathname = susfs_get_redirected_path(filp->f_inode->i_ino);
|
||||
if (!IS_ERR(fake_pathname)) {
|
||||
restore_nameidata();
|
||||
filp_close(filp, NULL);
|
||||
// no need to do `putname(pathname);` here as it will be done by calling process
|
||||
set_nameidata(&nd, dfd, fake_pathname);
|
||||
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
|
||||
if (unlikely(filp == ERR_PTR(-ECHILD)))
|
||||
filp = path_openat(&nd, op, flags);
|
||||
if (unlikely(filp == ERR_PTR(-ESTALE)))
|
||||
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
|
||||
restore_nameidata();
|
||||
putname(fake_pathname);
|
||||
return filp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
restore_nameidata();
|
||||
return filp;
|
||||
}
|
||||
|
||||
327
fs/namespace.c
327
fs/namespace.c
@@ -81,36 +81,6 @@
|
||||
#define ART_ALLOW 2
|
||||
#endif /*CONFIG_RKP_NS_PROT */
|
||||
|
||||
#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
|
||||
#include <linux/susfs_def.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
extern bool susfs_is_current_ksu_domain(void);
|
||||
extern bool susfs_is_current_zygote_domain(void);
|
||||
|
||||
static DEFINE_IDA(susfs_mnt_id_ida);
|
||||
static DEFINE_IDA(susfs_mnt_group_ida);
|
||||
static int susfs_mnt_id_start = DEFAULT_SUS_MNT_ID;
|
||||
static int susfs_mnt_group_start = DEFAULT_SUS_MNT_GROUP_ID;
|
||||
|
||||
#define CL_ZYGOTE_COPY_MNT_NS BIT(24) /* used by copy_mnt_ns() */
|
||||
#define CL_COPY_MNT_NS BIT(25) /* used by copy_mnt_ns() */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
|
||||
extern void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname);
|
||||
bool susfs_is_auto_add_sus_ksu_default_mount_enabled = true;
|
||||
#endif
|
||||
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
|
||||
extern int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target);
|
||||
bool susfs_is_auto_add_sus_bind_mount_enabled = true;
|
||||
#endif
|
||||
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
|
||||
extern void susfs_auto_add_try_umount_for_bind_mount(struct path *path);
|
||||
bool susfs_is_auto_add_try_umount_for_bind_mount_enabled = true;
|
||||
#endif
|
||||
|
||||
/* Maximum number of mounts in a mount namespace */
|
||||
unsigned int sysctl_mount_max __read_mostly = 100000;
|
||||
|
||||
@@ -334,25 +304,6 @@ static inline struct hlist_head *mp_hash(struct dentry *dentry)
|
||||
return &mountpoint_hashtable[tmp & mp_hash_mask];
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// Our own mnt_alloc_id() that assigns mnt_id starting from DEFAULT_SUS_MNT_ID
|
||||
static int susfs_mnt_alloc_id(struct mount *mnt)
|
||||
{
|
||||
int res;
|
||||
|
||||
retry:
|
||||
ida_pre_get(&susfs_mnt_id_ida, GFP_KERNEL);
|
||||
spin_lock(&mnt_id_lock);
|
||||
res = ida_get_new_above(&susfs_mnt_id_ida, susfs_mnt_id_start, &mnt->mnt_id);
|
||||
if (!res)
|
||||
susfs_mnt_id_start = mnt->mnt_id + 1;
|
||||
spin_unlock(&mnt_id_lock);
|
||||
if (res == -EAGAIN)
|
||||
goto retry;
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
static int mnt_alloc_id(struct mount *mnt)
|
||||
{
|
||||
int res;
|
||||
@@ -396,35 +347,6 @@ static int mnt_alloc_vfsmount(struct mount *mnt)
|
||||
static void mnt_free_id(struct mount *mnt)
|
||||
{
|
||||
int id = mnt->mnt_id;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
int mnt_id_backup = mnt->mnt.susfs_mnt_id_backup;
|
||||
// We should first check the 'mnt->mnt.susfs_mnt_id_backup', see if it is DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE
|
||||
// if so, these mnt_id were not assigned by mnt_alloc_id() so we don't need to free it.
|
||||
if (unlikely(mnt_id_backup == DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE)) {
|
||||
return;
|
||||
}
|
||||
// Now we can check if its mnt_id is sus
|
||||
if (unlikely(mnt->mnt_id >= DEFAULT_SUS_MNT_ID)) {
|
||||
spin_lock(&mnt_id_lock);
|
||||
ida_remove(&susfs_mnt_id_ida, id);
|
||||
if (susfs_mnt_id_start > id)
|
||||
susfs_mnt_id_start = id;
|
||||
spin_unlock(&mnt_id_lock);
|
||||
return;
|
||||
}
|
||||
// Lastly if 'mnt->mnt.susfs_mnt_id_backup' is not 0, then it contains a backup origin mnt_id
|
||||
// so we free it in the original way
|
||||
if (likely(mnt_id_backup)) {
|
||||
// If mnt->mnt.susfs_mnt_id_backup is not zero, it means mnt->mnt_id is spoofed,
|
||||
// so here we return the original mnt_id for being freed.
|
||||
spin_lock(&mnt_id_lock);
|
||||
ida_remove(&mnt_id_ida, mnt_id_backup);
|
||||
if (mnt_id_start > mnt_id_backup)
|
||||
mnt_id_start = mnt_id_backup;
|
||||
spin_unlock(&mnt_id_lock);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
spin_lock(&mnt_id_lock);
|
||||
ida_remove(&mnt_id_ida, id);
|
||||
if (mnt_id_start > id)
|
||||
@@ -441,19 +363,6 @@ static int mnt_alloc_group_id(struct mount *mnt)
|
||||
{
|
||||
int res;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
if (mnt->mnt_id >= DEFAULT_SUS_MNT_ID) {
|
||||
if (!ida_pre_get(&susfs_mnt_group_ida, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
// If so, assign a sus mnt_group id DEFAULT_SUS_MNT_GROUP_ID from susfs_mnt_group_ida
|
||||
res = ida_get_new_above(&susfs_mnt_group_ida,
|
||||
susfs_mnt_group_start,
|
||||
&mnt->mnt_group_id);
|
||||
if (!res)
|
||||
susfs_mnt_group_start = mnt->mnt_group_id + 1;
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -472,17 +381,6 @@ static int mnt_alloc_group_id(struct mount *mnt)
|
||||
void mnt_release_group_id(struct mount *mnt)
|
||||
{
|
||||
int id = mnt->mnt_group_id;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// If mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID, it means 'mnt' is also sus mount,
|
||||
// then we free the mnt->mnt_group_id from susfs_mnt_group_ida
|
||||
if (id >= DEFAULT_SUS_MNT_GROUP_ID) {
|
||||
ida_remove(&susfs_mnt_group_ida, id);
|
||||
if (susfs_mnt_group_start > id)
|
||||
susfs_mnt_group_start = id;
|
||||
mnt->mnt_group_id = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ida_remove(&mnt_group_ida, id);
|
||||
if (mnt_group_start > id)
|
||||
mnt_group_start = id;
|
||||
@@ -534,31 +432,13 @@ static void drop_mountpoint(struct fs_pin *p)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
static struct mount *alloc_vfsmnt(const char *name, bool should_spoof, int custom_mnt_id)
|
||||
#else
|
||||
static struct mount *alloc_vfsmnt(const char *name)
|
||||
#endif
|
||||
{
|
||||
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
|
||||
if (mnt) {
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
if (should_spoof) {
|
||||
if (!custom_mnt_id) {
|
||||
err = susfs_mnt_alloc_id(mnt);
|
||||
} else {
|
||||
mnt->mnt_id = custom_mnt_id;
|
||||
err = 0;
|
||||
}
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
#endif
|
||||
err = mnt_alloc_id(mnt);
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
bypass_orig_flow:
|
||||
#endif
|
||||
if (err)
|
||||
goto out_free_cache;
|
||||
#ifdef CONFIG_RKP_NS_PROT
|
||||
@@ -1461,17 +1341,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
||||
if (!type)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// For newly created mounts, the only caller process we care is KSU
|
||||
if (unlikely(susfs_is_current_ksu_domain())) {
|
||||
mnt = alloc_vfsmnt(name, true, 0);
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
mnt = alloc_vfsmnt(name, false, 0);
|
||||
bypass_orig_flow:
|
||||
#else
|
||||
mnt = alloc_vfsmnt(name);
|
||||
#endif
|
||||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
#ifdef CONFIG_RKP_NS_PROT
|
||||
@@ -1515,15 +1385,6 @@ bypass_orig_flow:
|
||||
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
||||
#endif
|
||||
mnt->mnt_parent = mnt;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// If caller process is zygote, then it is a normal mount, so we just reorder the mnt_id
|
||||
if (susfs_is_current_zygote_domain()) {
|
||||
mnt->mnt.susfs_mnt_id_backup = mnt->mnt_id;
|
||||
mnt->mnt_id = current->susfs_last_fake_mnt_id++;
|
||||
}
|
||||
#endif
|
||||
|
||||
lock_mount_hash();
|
||||
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
|
||||
unlock_mount_hash();
|
||||
@@ -1564,52 +1425,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
||||
int nsflags;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
bool is_current_ksu_domain = susfs_is_current_ksu_domain();
|
||||
bool is_current_zygote_domain = susfs_is_current_zygote_domain();
|
||||
|
||||
/* - It is very important that we need to use CL_COPY_MNT_NS to identify whether
|
||||
* the clone is a copy_tree() or single mount like called by __do_loopback()
|
||||
* - if caller process is KSU, consider the following situation:
|
||||
* 1. it is NOT doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id
|
||||
* 2. it is doing unshare => spoof the new mnt_id with the old mnt_id
|
||||
* - If caller process is zygote and old mnt_id is sus => call alloc_vfsmnt() to assign a new sus mnt_id
|
||||
* - For the rest of caller process that doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id only for old sus mount
|
||||
*/
|
||||
// Firstly, check if it is KSU process
|
||||
if (unlikely(is_current_ksu_domain)) {
|
||||
// if it is doing single clone
|
||||
if (!(flag & CL_COPY_MNT_NS)) {
|
||||
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
// if it is doing unshare
|
||||
mnt = alloc_vfsmnt(old->mnt_devname, true, old->mnt_id);
|
||||
if (mnt) {
|
||||
mnt->mnt.susfs_mnt_id_backup = DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE;
|
||||
}
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
// Secondly, check if it is zygote process and no matter it is doing unshare or not
|
||||
if (likely(is_current_zygote_domain) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
|
||||
/* Important Note:
|
||||
* - Here we can't determine whether the unshare is called zygisk or not,
|
||||
* so we can only patch out the unshare code in zygisk source code for now
|
||||
* - But at least we can deal with old sus mounts using alloc_vfsmnt()
|
||||
*/
|
||||
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
// Lastly, for other process that is doing unshare operation, but only deal with old sus mount
|
||||
if ((flag & CL_COPY_MNT_NS) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
|
||||
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
mnt = alloc_vfsmnt(old->mnt_devname, false, 0);
|
||||
bypass_orig_flow:
|
||||
#else
|
||||
mnt = alloc_vfsmnt(old->mnt_devname);
|
||||
#endif
|
||||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -1696,15 +1512,6 @@ bypass_orig_flow:
|
||||
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
||||
#endif
|
||||
mnt->mnt_parent = mnt;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// If caller process is zygote and not doing unshare, so we just reorder the mnt_id
|
||||
if (likely(is_current_zygote_domain) && !(flag & CL_ZYGOTE_COPY_MNT_NS)) {
|
||||
mnt->mnt.susfs_mnt_id_backup = mnt->mnt_id;
|
||||
mnt->mnt_id = current->susfs_last_fake_mnt_id++;
|
||||
}
|
||||
#endif
|
||||
|
||||
lock_mount_hash();
|
||||
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
|
||||
unlock_mount_hash();
|
||||
@@ -2371,40 +2178,6 @@ static inline bool may_mandlock(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int can_umount(const struct path *path, int flags)
|
||||
{
|
||||
struct mount *mnt = real_mount(path->mnt);
|
||||
|
||||
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
|
||||
return -EINVAL;
|
||||
if (!may_mount())
|
||||
return -EPERM;
|
||||
if (path->dentry != path->mnt->mnt_root)
|
||||
return -EINVAL;
|
||||
if (!check_mnt(mnt))
|
||||
return -EINVAL;
|
||||
if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */
|
||||
return -EINVAL;
|
||||
if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int path_umount(struct path *path, int flags)
|
||||
{
|
||||
struct mount *mnt = real_mount(path->mnt);
|
||||
int ret;
|
||||
|
||||
ret = can_umount(path, flags);
|
||||
if (!ret)
|
||||
ret = do_umount(mnt, flags);
|
||||
|
||||
/* we mustn't call path_put() as that would clear mnt_expiry_mark */
|
||||
dput(path->dentry);
|
||||
mntput_no_expire(mnt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now umount can handle mount points as well as block devices.
|
||||
* This is important for filesystems which use unnamed block devices.
|
||||
@@ -3090,27 +2863,6 @@ static int do_loopback(struct path *path, const char *old_name,
|
||||
umount_tree(mnt, UMOUNT_SYNC);
|
||||
unlock_mount_hash();
|
||||
}
|
||||
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
|
||||
// Check if bind mounted path should be hidden and umounted automatically.
|
||||
// And we target only process with ksu domain.
|
||||
if (susfs_is_current_ksu_domain()) {
|
||||
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
|
||||
if (susfs_is_auto_add_sus_bind_mount_enabled &&
|
||||
susfs_auto_add_sus_bind_mount(old_name, &old_path)) {
|
||||
goto orig_flow;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
|
||||
if (susfs_is_auto_add_try_umount_for_bind_mount_enabled) {
|
||||
susfs_auto_add_try_umount_for_bind_mount(path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
|
||||
orig_flow:
|
||||
#endif
|
||||
#endif // #if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
|
||||
|
||||
out2:
|
||||
unlock_mount(mp);
|
||||
out:
|
||||
@@ -3829,15 +3581,6 @@ long do_mount(const char *dev_name, const char __user *dir_name,
|
||||
else
|
||||
retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
|
||||
dev_name, data_page);
|
||||
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
|
||||
// For both Legacy and Magic Mount KernelSU
|
||||
if (!retval && susfs_is_auto_add_sus_ksu_default_mount_enabled &&
|
||||
(!(flags & (MS_REMOUNT | MS_BIND | MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)))) {
|
||||
if (susfs_is_current_ksu_domain()) {
|
||||
susfs_auto_add_sus_ksu_default_mount(dir_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
dput_out:
|
||||
path_put(&path);
|
||||
return retval;
|
||||
@@ -3915,10 +3658,6 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
struct mount *old;
|
||||
struct mount *new;
|
||||
int copy_flags;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
bool is_zygote_pid = susfs_is_current_zygote_domain();
|
||||
int last_entry_mnt_id = 0;
|
||||
#endif
|
||||
|
||||
BUG_ON(!ns);
|
||||
|
||||
@@ -3938,15 +3677,6 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
|
||||
if (user_ns != ns->user_ns)
|
||||
copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// Always let clone_mnt() in copy_tree() know it is from copy_mnt_ns()
|
||||
copy_flags |= CL_COPY_MNT_NS;
|
||||
if (is_zygote_pid) {
|
||||
// Let clone_mnt() in copy_tree() know copy_mnt_ns() is run by zygote process
|
||||
copy_flags |= CL_ZYGOTE_COPY_MNT_NS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKP_NS_PROT
|
||||
new = copy_tree(old, old->mnt->mnt_root, copy_flags);
|
||||
#else
|
||||
@@ -4003,29 +3733,6 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
#endif
|
||||
p = next_mnt(p, old);
|
||||
}
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
// current->susfs_last_fake_mnt_id -> to record last valid fake mnt_id to zygote pid
|
||||
// q->mnt.susfs_mnt_id_backup -> original mnt_id
|
||||
// q->mnt_id -> will be modified to the fake mnt_id
|
||||
|
||||
// Here We are only interested in processes of which original mnt namespace belongs to zygote
|
||||
// Also we just make use of existing 'q' mount pointer, no need to delcare extra mount pointer
|
||||
if (is_zygote_pid) {
|
||||
last_entry_mnt_id = list_first_entry(&new_ns->list, struct mount, mnt_list)->mnt_id;
|
||||
list_for_each_entry(q, &new_ns->list, mnt_list) {
|
||||
if (unlikely(q->mnt_id >= DEFAULT_SUS_MNT_ID)) {
|
||||
continue;
|
||||
}
|
||||
q->mnt.susfs_mnt_id_backup = q->mnt_id;
|
||||
q->mnt_id = last_entry_mnt_id++;
|
||||
}
|
||||
}
|
||||
// Assign the 'last_entry_mnt_id' to 'current->susfs_last_fake_mnt_id' for later use.
|
||||
// should be fine here assuming zygote is forking/unsharing app in one single thread.
|
||||
// Or should we put a lock here?
|
||||
current->susfs_last_fake_mnt_id = last_entry_mnt_id;
|
||||
#endif
|
||||
|
||||
namespace_unlock();
|
||||
|
||||
if (rootmnt)
|
||||
@@ -4656,37 +4363,3 @@ const struct proc_ns_operations mntns_operations = {
|
||||
.install = mntns_install,
|
||||
.owner = mntns_owner,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
||||
extern void susfs_try_umount_all(uid_t uid);
|
||||
void susfs_run_try_umount_for_current_mnt_ns(void) {
|
||||
struct mount *mnt;
|
||||
struct mnt_namespace *mnt_ns;
|
||||
|
||||
mnt_ns = current->nsproxy->mnt_ns;
|
||||
// Lock the namespace
|
||||
namespace_lock();
|
||||
list_for_each_entry(mnt, &mnt_ns->list, mnt_list) {
|
||||
// Change the sus mount to be private
|
||||
if (mnt->mnt_id >= DEFAULT_SUS_MNT_ID) {
|
||||
change_mnt_propagation(mnt, MS_PRIVATE);
|
||||
}
|
||||
}
|
||||
// Unlock the namespace
|
||||
namespace_unlock();
|
||||
susfs_try_umount_all(current_uid().val);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_KSU_SUSFS
|
||||
bool susfs_is_mnt_devname_ksu(struct path *path) {
|
||||
struct mount *mnt;
|
||||
|
||||
if (path && path->mnt) {
|
||||
mnt = real_mount(path->mnt);
|
||||
if (mnt && mnt->mnt_devname && !strcmp(mnt->mnt_devname, "KSU")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/exportfs.h>
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
#include <linux/susfs_def.h>
|
||||
#endif
|
||||
|
||||
#include "inotify/inotify.h"
|
||||
#include "../fs/mount.h"
|
||||
@@ -24,27 +21,16 @@
|
||||
|
||||
#if defined(CONFIG_INOTIFY_USER) || defined(CONFIG_FANOTIFY)
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
static void show_fdinfo(struct seq_file *m, struct file *f,
|
||||
void (*show)(struct seq_file *m,
|
||||
struct fsnotify_mark *mark,
|
||||
struct file *file))
|
||||
#else
|
||||
static void show_fdinfo(struct seq_file *m, struct file *f,
|
||||
void (*show)(struct seq_file *m,
|
||||
struct fsnotify_mark *mark))
|
||||
#endif
|
||||
{
|
||||
struct fsnotify_group *group = f->private_data;
|
||||
struct fsnotify_mark *mark;
|
||||
|
||||
mutex_lock(&group->mark_mutex);
|
||||
list_for_each_entry(mark, &group->marks_list, g_list) {
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
show(m, mark, f);
|
||||
#else
|
||||
show(m, mark);
|
||||
#endif
|
||||
if (seq_has_overflowed(m))
|
||||
break;
|
||||
}
|
||||
@@ -86,11 +72,7 @@ static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
|
||||
|
||||
#ifdef CONFIG_INOTIFY_USER
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark, struct file *file)
|
||||
#else
|
||||
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
|
||||
#endif
|
||||
{
|
||||
struct inotify_inode_mark *inode_mark;
|
||||
struct inode *inode;
|
||||
@@ -101,36 +83,6 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
|
||||
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
|
||||
inode = igrab(mark->connector->inode);
|
||||
if (inode) {
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) &&
|
||||
unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) {
|
||||
struct path path;
|
||||
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
char *dpath;
|
||||
if (!pathname) {
|
||||
goto out_seq_printf;
|
||||
}
|
||||
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
|
||||
if (!dpath) {
|
||||
goto out_free_pathname;
|
||||
}
|
||||
if (kern_path(dpath, 0, &path)) {
|
||||
goto out_free_pathname;
|
||||
}
|
||||
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
|
||||
inode_mark->wd, path.dentry->d_inode->i_ino, path.dentry->d_inode->i_sb->s_dev,
|
||||
inotify_mark_user_mask(mark));
|
||||
show_mark_fhandle(m, path.dentry->d_inode);
|
||||
seq_putc(m, '\n');
|
||||
iput(inode);
|
||||
path_put(&path);
|
||||
kfree(pathname);
|
||||
return;
|
||||
out_free_pathname:
|
||||
kfree(pathname);
|
||||
}
|
||||
out_seq_printf:
|
||||
#endif
|
||||
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
|
||||
inode_mark->wd, inode->i_ino, inode->i_sb->s_dev,
|
||||
inotify_mark_user_mask(mark));
|
||||
|
||||
@@ -358,11 +358,6 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* access() needs to use the real uid/gid, not the effective uid/gid.
|
||||
* We do this by temporarily clearing all FS-related capabilities and
|
||||
@@ -378,10 +373,6 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
#ifdef CONFIG_KSU
|
||||
ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
|
||||
#endif
|
||||
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -69,14 +69,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
|
||||
bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
|
||||
ovl_path_lowerdata(dentry, &realpath);
|
||||
if (likely(realpath.mnt && realpath.dentry)) {
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = vfs_getattr(&realpath, stat, request_mask, flags);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
type = ovl_path_real(dentry, &realpath);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = vfs_getattr(&realpath, stat, request_mask, flags);
|
||||
|
||||
@@ -199,9 +199,6 @@ bool ovl_dentry_weird(struct dentry *dentry);
|
||||
enum ovl_path_type ovl_path_type(struct dentry *dentry);
|
||||
void ovl_path_upper(struct dentry *dentry, struct path *path);
|
||||
void ovl_path_lower(struct dentry *dentry, struct path *path);
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
|
||||
void ovl_path_lowerdata(struct dentry *dentry, struct path *path);
|
||||
#endif
|
||||
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
|
||||
struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
||||
|
||||
@@ -838,19 +838,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
|
||||
if (!od)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
|
||||
ovl_path_lowerdata(file->f_path.dentry, &realpath);
|
||||
if (likely(realpath.mnt && realpath.dentry)) {
|
||||
// We still use '__OVL_PATH_UPPER' here which should be fine.
|
||||
type = __OVL_PATH_UPPER;
|
||||
goto bypass_orig_flow;
|
||||
}
|
||||
#endif
|
||||
|
||||
type = ovl_path_real(file->f_path.dentry, &realpath);
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
|
||||
bypass_orig_flow:
|
||||
#endif
|
||||
realfile = ovl_path_open(&realpath, file->f_flags);
|
||||
if (IS_ERR(realfile)) {
|
||||
kfree(od);
|
||||
|
||||
@@ -287,18 +287,6 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
|
||||
ovl_path_lowerdata(root_dentry, &path);
|
||||
if (likely(path.mnt && path.dentry)) {
|
||||
err = vfs_statfs(&path, buf);
|
||||
if (!err) {
|
||||
buf->f_namelen = 255; // 255 for erofs, ext2/4, f2fs
|
||||
buf->f_type = path.dentry->d_sb->s_magic;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
ovl_path_real(root_dentry, &path);
|
||||
|
||||
err = vfs_statfs(&path, buf);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user