mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-11 15:38:07 +09:00
Merge branch 'android-3.10' of https://android.googlesource.com/kernel/common
* android-3.10: (23 commits) proc: uid_cputime: create uids from kuids wakeup: Add last wake up source logging for suspend abort reason. selinux: add SOCK_DIAG_BY_FAMILY to the list of netlink message types selinux/nlmsg: add XFRM_MSG_MAPPING selinux/nlmsg: add XFRM_MSG_MIGRATE selinux/nlmsg: add XFRM_MSG_REPORT selinux/nlmsg: add XFRM_MSG_[NEW|GET]SADINFO selinux/nlmsg: add XFRM_MSG_GETSPDINFO selinux/nlmsg: add XFRM_MSG_NEWSPDINFO SELinux: per-command whitelisting of ioctls security: lsm_audit: add ioctl specific auditing Power: Report suspend times from last_suspend_time arm: crypto: Add optimized SHA-256/224 proc: uid: Adds accounting for the cputimes per uid. USB: gadget: android: Integrate f_midi USB MIDI gadget driver part deux android: base-cfg: disable ALARM_DEV staging: Remove logger and alarm-dev from android Makefile staging: Remove the Android alarm-dev driver staging: Remove the Android logger driver net: ping: fix constness inconsistency in ipv6_chk_addr ...
This commit is contained in:
16
Documentation/ABI/testing/sysfs-kernel-wakeup_reasons
Normal file
16
Documentation/ABI/testing/sysfs-kernel-wakeup_reasons
Normal file
@@ -0,0 +1,16 @@
|
||||
What: /sys/kernel/wakeup_reasons/last_resume_reason
|
||||
Date: February 2014
|
||||
Contact: Ruchi Kandoi <kandoiruchi@google.com>
|
||||
Description:
|
||||
The /sys/kernel/wakeup_reasons/last_resume_reason is
|
||||
used to report wakeup reasons after system exited suspend.
|
||||
|
||||
What: /sys/kernel/wakeup_reasons/last_suspend_time
|
||||
Date: March 2015
|
||||
Contact: jinqian <jinqian@google.com>
|
||||
Description:
|
||||
The /sys/kernel/wakeup_reasons/last_suspend_time is
|
||||
used to report time spent in last suspend cycle. It contains
|
||||
two numbers (in seconds) separated by space. First number is
|
||||
the time spent in suspend and resume processes. Second number
|
||||
is the time spent in sleep state.
|
||||
@@ -6,7 +6,6 @@
|
||||
# CONFIG_OABI_COMPAT is not set
|
||||
CONFIG_ANDROID=y
|
||||
CONFIG_ANDROID_BINDER_IPC=y
|
||||
CONFIG_ANDROID_INTF_ALARM_DEV=y
|
||||
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
|
||||
CONFIG_ARMV7_COMPAT=y
|
||||
CONFIG_ASHMEM=y
|
||||
|
||||
@@ -6,12 +6,15 @@ obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o
|
||||
obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
|
||||
obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o
|
||||
obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
|
||||
obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o
|
||||
obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o
|
||||
|
||||
aes-arm-y := aes-armv4.o aes_glue.o
|
||||
aes-arm-bs-y := aesbs-core.o aesbs-glue.o
|
||||
sha1-arm-y := sha1-armv4-large.o sha1_glue.o
|
||||
sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o
|
||||
sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o
|
||||
sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y)
|
||||
sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o
|
||||
|
||||
quiet_cmd_perl = PERL $@
|
||||
@@ -20,4 +23,7 @@ quiet_cmd_perl = PERL $@
|
||||
$(src)/aesbs-core.S_shipped: $(src)/bsaes-armv7.pl
|
||||
$(call cmd,perl)
|
||||
|
||||
.PRECIOUS: $(obj)/aesbs-core.S
|
||||
$(src)/sha256-core.S_shipped: $(src)/sha256-armv4.pl
|
||||
$(call cmd,perl)
|
||||
|
||||
.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S
|
||||
|
||||
716
arch/arm/crypto/sha256-armv4.pl
Normal file
716
arch/arm/crypto/sha256-armv4.pl
Normal file
@@ -0,0 +1,716 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# ====================================================================
|
||||
# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||
# project. The module is, however, dual licensed under OpenSSL and
|
||||
# CRYPTOGAMS licenses depending on where you obtain it. For further
|
||||
# details see http://www.openssl.org/~appro/cryptogams/.
|
||||
#
|
||||
# Permission to use under GPL terms is granted.
|
||||
# ====================================================================
|
||||
|
||||
# SHA256 block procedure for ARMv4. May 2007.
|
||||
|
||||
# Performance is ~2x better than gcc 3.4 generated code and in "abso-
|
||||
# lute" terms is ~2250 cycles per 64-byte block or ~35 cycles per
|
||||
# byte [on single-issue Xscale PXA250 core].
|
||||
|
||||
# July 2010.
|
||||
#
|
||||
# Rescheduling for dual-issue pipeline resulted in 22% improvement on
|
||||
# Cortex A8 core and ~20 cycles per processed byte.
|
||||
|
||||
# February 2011.
|
||||
#
|
||||
# Profiler-assisted and platform-specific optimization resulted in 16%
|
||||
# improvement on Cortex A8 core and ~15.4 cycles per processed byte.
|
||||
|
||||
# September 2013.
|
||||
#
|
||||
# Add NEON implementation. On Cortex A8 it was measured to process one
|
||||
# byte in 12.5 cycles or 23% faster than integer-only code. Snapdragon
|
||||
# S4 does it in 12.5 cycles too, but it's 50% faster than integer-only
|
||||
# code (meaning that latter performs sub-optimally, nothing was done
|
||||
# about it).
|
||||
|
||||
# May 2014.
|
||||
#
|
||||
# Add ARMv8 code path performing at 2.0 cpb on Apple A7.
|
||||
|
||||
while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
|
||||
open STDOUT,">$output";
|
||||
|
||||
$ctx="r0"; $t0="r0";
|
||||
$inp="r1"; $t4="r1";
|
||||
$len="r2"; $t1="r2";
|
||||
$T1="r3"; $t3="r3";
|
||||
$A="r4";
|
||||
$B="r5";
|
||||
$C="r6";
|
||||
$D="r7";
|
||||
$E="r8";
|
||||
$F="r9";
|
||||
$G="r10";
|
||||
$H="r11";
|
||||
@V=($A,$B,$C,$D,$E,$F,$G,$H);
|
||||
$t2="r12";
|
||||
$Ktbl="r14";
|
||||
|
||||
@Sigma0=( 2,13,22);
|
||||
@Sigma1=( 6,11,25);
|
||||
@sigma0=( 7,18, 3);
|
||||
@sigma1=(17,19,10);
|
||||
|
||||
sub BODY_00_15 {
|
||||
my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
|
||||
|
||||
$code.=<<___ if ($i<16);
|
||||
#if __ARM_ARCH__>=7
|
||||
@ ldr $t1,[$inp],#4 @ $i
|
||||
# if $i==15
|
||||
str $inp,[sp,#17*4] @ make room for $t4
|
||||
# endif
|
||||
eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
|
||||
add $a,$a,$t2 @ h+=Maj(a,b,c) from the past
|
||||
eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e)
|
||||
# ifndef __ARMEB__
|
||||
rev $t1,$t1
|
||||
# endif
|
||||
#else
|
||||
@ ldrb $t1,[$inp,#3] @ $i
|
||||
add $a,$a,$t2 @ h+=Maj(a,b,c) from the past
|
||||
ldrb $t2,[$inp,#2]
|
||||
ldrb $t0,[$inp,#1]
|
||||
orr $t1,$t1,$t2,lsl#8
|
||||
ldrb $t2,[$inp],#4
|
||||
orr $t1,$t1,$t0,lsl#16
|
||||
# if $i==15
|
||||
str $inp,[sp,#17*4] @ make room for $t4
|
||||
# endif
|
||||
eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
|
||||
orr $t1,$t1,$t2,lsl#24
|
||||
eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e)
|
||||
#endif
|
||||
___
|
||||
$code.=<<___;
|
||||
ldr $t2,[$Ktbl],#4 @ *K256++
|
||||
add $h,$h,$t1 @ h+=X[i]
|
||||
str $t1,[sp,#`$i%16`*4]
|
||||
eor $t1,$f,$g
|
||||
add $h,$h,$t0,ror#$Sigma1[0] @ h+=Sigma1(e)
|
||||
and $t1,$t1,$e
|
||||
add $h,$h,$t2 @ h+=K256[i]
|
||||
eor $t1,$t1,$g @ Ch(e,f,g)
|
||||
eor $t0,$a,$a,ror#`$Sigma0[1]-$Sigma0[0]`
|
||||
add $h,$h,$t1 @ h+=Ch(e,f,g)
|
||||
#if $i==31
|
||||
and $t2,$t2,#0xff
|
||||
cmp $t2,#0xf2 @ done?
|
||||
#endif
|
||||
#if $i<15
|
||||
# if __ARM_ARCH__>=7
|
||||
ldr $t1,[$inp],#4 @ prefetch
|
||||
# else
|
||||
ldrb $t1,[$inp,#3]
|
||||
# endif
|
||||
eor $t2,$a,$b @ a^b, b^c in next round
|
||||
#else
|
||||
ldr $t1,[sp,#`($i+2)%16`*4] @ from future BODY_16_xx
|
||||
eor $t2,$a,$b @ a^b, b^c in next round
|
||||
ldr $t4,[sp,#`($i+15)%16`*4] @ from future BODY_16_xx
|
||||
#endif
|
||||
eor $t0,$t0,$a,ror#`$Sigma0[2]-$Sigma0[0]` @ Sigma0(a)
|
||||
and $t3,$t3,$t2 @ (b^c)&=(a^b)
|
||||
add $d,$d,$h @ d+=h
|
||||
eor $t3,$t3,$b @ Maj(a,b,c)
|
||||
add $h,$h,$t0,ror#$Sigma0[0] @ h+=Sigma0(a)
|
||||
@ add $h,$h,$t3 @ h+=Maj(a,b,c)
|
||||
___
|
||||
($t2,$t3)=($t3,$t2);
|
||||
}
|
||||
|
||||
sub BODY_16_XX {
|
||||
my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
|
||||
|
||||
$code.=<<___;
|
||||
@ ldr $t1,[sp,#`($i+1)%16`*4] @ $i
|
||||
@ ldr $t4,[sp,#`($i+14)%16`*4]
|
||||
mov $t0,$t1,ror#$sigma0[0]
|
||||
add $a,$a,$t2 @ h+=Maj(a,b,c) from the past
|
||||
mov $t2,$t4,ror#$sigma1[0]
|
||||
eor $t0,$t0,$t1,ror#$sigma0[1]
|
||||
eor $t2,$t2,$t4,ror#$sigma1[1]
|
||||
eor $t0,$t0,$t1,lsr#$sigma0[2] @ sigma0(X[i+1])
|
||||
ldr $t1,[sp,#`($i+0)%16`*4]
|
||||
eor $t2,$t2,$t4,lsr#$sigma1[2] @ sigma1(X[i+14])
|
||||
ldr $t4,[sp,#`($i+9)%16`*4]
|
||||
|
||||
add $t2,$t2,$t0
|
||||
eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]` @ from BODY_00_15
|
||||
add $t1,$t1,$t2
|
||||
eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e)
|
||||
add $t1,$t1,$t4 @ X[i]
|
||||
___
|
||||
&BODY_00_15(@_);
|
||||
}
|
||||
|
||||
$code=<<___;
|
||||
#ifndef __KERNEL__
|
||||
# include "arm_arch.h"
|
||||
#else
|
||||
# define __ARM_ARCH__ __LINUX_ARM_ARCH__
|
||||
# define __ARM_MAX_ARCH__ 7
|
||||
#endif
|
||||
|
||||
.text
|
||||
#if __ARM_ARCH__<7
|
||||
.code 32
|
||||
#else
|
||||
.syntax unified
|
||||
# ifdef __thumb2__
|
||||
# define adrl adr
|
||||
.thumb
|
||||
# else
|
||||
.code 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
.type K256,%object
|
||||
.align 5
|
||||
K256:
|
||||
.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
|
||||
.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
|
||||
.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
|
||||
.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
|
||||
.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
|
||||
.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
|
||||
.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
|
||||
.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
|
||||
.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
|
||||
.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
|
||||
.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
|
||||
.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070
|
||||
.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
|
||||
.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
|
||||
.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
|
||||
.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
|
||||
.size K256,.-K256
|
||||
.word 0 @ terminator
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
.LOPENSSL_armcap:
|
||||
.word OPENSSL_armcap_P-sha256_block_data_order
|
||||
#endif
|
||||
.align 5
|
||||
|
||||
.global sha256_block_data_order
|
||||
.type sha256_block_data_order,%function
|
||||
sha256_block_data_order:
|
||||
#if __ARM_ARCH__<7
|
||||
sub r3,pc,#8 @ sha256_block_data_order
|
||||
#else
|
||||
adr r3,sha256_block_data_order
|
||||
#endif
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
ldr r12,.LOPENSSL_armcap
|
||||
ldr r12,[r3,r12] @ OPENSSL_armcap_P
|
||||
tst r12,#ARMV8_SHA256
|
||||
bne .LARMv8
|
||||
tst r12,#ARMV7_NEON
|
||||
bne .LNEON
|
||||
#endif
|
||||
add $len,$inp,$len,lsl#6 @ len to point at the end of inp
|
||||
stmdb sp!,{$ctx,$inp,$len,r4-r11,lr}
|
||||
ldmia $ctx,{$A,$B,$C,$D,$E,$F,$G,$H}
|
||||
sub $Ktbl,r3,#256+32 @ K256
|
||||
sub sp,sp,#16*4 @ alloca(X[16])
|
||||
.Loop:
|
||||
# if __ARM_ARCH__>=7
|
||||
ldr $t1,[$inp],#4
|
||||
# else
|
||||
ldrb $t1,[$inp,#3]
|
||||
# endif
|
||||
eor $t3,$B,$C @ magic
|
||||
eor $t2,$t2,$t2
|
||||
___
|
||||
for($i=0;$i<16;$i++) { &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
|
||||
$code.=".Lrounds_16_xx:\n";
|
||||
for (;$i<32;$i++) { &BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
|
||||
$code.=<<___;
|
||||
#if __ARM_ARCH__>=7
|
||||
ite eq @ Thumb2 thing, sanity check in ARM
|
||||
#endif
|
||||
ldreq $t3,[sp,#16*4] @ pull ctx
|
||||
bne .Lrounds_16_xx
|
||||
|
||||
add $A,$A,$t2 @ h+=Maj(a,b,c) from the past
|
||||
ldr $t0,[$t3,#0]
|
||||
ldr $t1,[$t3,#4]
|
||||
ldr $t2,[$t3,#8]
|
||||
add $A,$A,$t0
|
||||
ldr $t0,[$t3,#12]
|
||||
add $B,$B,$t1
|
||||
ldr $t1,[$t3,#16]
|
||||
add $C,$C,$t2
|
||||
ldr $t2,[$t3,#20]
|
||||
add $D,$D,$t0
|
||||
ldr $t0,[$t3,#24]
|
||||
add $E,$E,$t1
|
||||
ldr $t1,[$t3,#28]
|
||||
add $F,$F,$t2
|
||||
ldr $inp,[sp,#17*4] @ pull inp
|
||||
ldr $t2,[sp,#18*4] @ pull inp+len
|
||||
add $G,$G,$t0
|
||||
add $H,$H,$t1
|
||||
stmia $t3,{$A,$B,$C,$D,$E,$F,$G,$H}
|
||||
cmp $inp,$t2
|
||||
sub $Ktbl,$Ktbl,#256 @ rewind Ktbl
|
||||
bne .Loop
|
||||
|
||||
add sp,sp,#`16+3`*4 @ destroy frame
|
||||
#if __ARM_ARCH__>=5
|
||||
ldmia sp!,{r4-r11,pc}
|
||||
#else
|
||||
ldmia sp!,{r4-r11,lr}
|
||||
tst lr,#1
|
||||
moveq pc,lr @ be binary compatible with V4, yet
|
||||
bx lr @ interoperable with Thumb ISA:-)
|
||||
#endif
|
||||
.size sha256_block_data_order,.-sha256_block_data_order
|
||||
___
|
||||
######################################################################
|
||||
# NEON stuff
|
||||
#
|
||||
{{{
|
||||
my @X=map("q$_",(0..3));
|
||||
my ($T0,$T1,$T2,$T3,$T4,$T5)=("q8","q9","q10","q11","d24","d25");
|
||||
my $Xfer=$t4;
|
||||
my $j=0;
|
||||
|
||||
sub Dlo() { shift=~m|q([1]?[0-9])|?"d".($1*2):""; }
|
||||
sub Dhi() { shift=~m|q([1]?[0-9])|?"d".($1*2+1):""; }
|
||||
|
||||
sub AUTOLOAD() # thunk [simplified] x86-style perlasm
|
||||
{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
|
||||
my $arg = pop;
|
||||
$arg = "#$arg" if ($arg*1 eq $arg);
|
||||
$code .= "\t$opcode\t".join(',',@_,$arg)."\n";
|
||||
}
|
||||
|
||||
sub Xupdate()
|
||||
{ use integer;
|
||||
my $body = shift;
|
||||
my @insns = (&$body,&$body,&$body,&$body);
|
||||
my ($a,$b,$c,$d,$e,$f,$g,$h);
|
||||
|
||||
&vext_8 ($T0,@X[0],@X[1],4); # X[1..4]
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vext_8 ($T1,@X[2],@X[3],4); # X[9..12]
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T2,$T0,$sigma0[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 (@X[0],@X[0],$T1); # X[0..3] += X[9..12]
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T1,$T0,$sigma0[2]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T2,$T0,32-$sigma0[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T3,$T0,$sigma0[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T1,$T1,$T2);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T3,$T0,32-$sigma0[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T4,&Dhi(@X[3]),$sigma1[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T1,$T1,$T3); # sigma0(X[1..4])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T4,&Dhi(@X[3]),32-$sigma1[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T5,&Dhi(@X[3]),$sigma1[2]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 (@X[0],@X[0],$T1); # X[0..3] += sigma0(X[1..4])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T5,$T5,$T4);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T4,&Dhi(@X[3]),$sigma1[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T4,&Dhi(@X[3]),32-$sigma1[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T5,$T5,$T4); # sigma1(X[14..15])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 (&Dlo(@X[0]),&Dlo(@X[0]),$T5);# X[0..1] += sigma1(X[14..15])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T4,&Dlo(@X[0]),$sigma1[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T4,&Dlo(@X[0]),32-$sigma1[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T5,&Dlo(@X[0]),$sigma1[2]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T5,$T5,$T4);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vshr_u32 ($T4,&Dlo(@X[0]),$sigma1[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vld1_32 ("{$T0}","[$Ktbl,:128]!");
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vsli_32 ($T4,&Dlo(@X[0]),32-$sigma1[1]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&veor ($T5,$T5,$T4); # sigma1(X[16..17])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 (&Dhi(@X[0]),&Dhi(@X[0]),$T5);# X[2..3] += sigma1(X[16..17])
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 ($T0,$T0,@X[0]);
|
||||
while($#insns>=2) { eval(shift(@insns)); }
|
||||
&vst1_32 ("{$T0}","[$Xfer,:128]!");
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
|
||||
push(@X,shift(@X)); # "rotate" X[]
|
||||
}
|
||||
|
||||
sub Xpreload()
|
||||
{ use integer;
|
||||
my $body = shift;
|
||||
my @insns = (&$body,&$body,&$body,&$body);
|
||||
my ($a,$b,$c,$d,$e,$f,$g,$h);
|
||||
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vld1_32 ("{$T0}","[$Ktbl,:128]!");
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vrev32_8 (@X[0],@X[0]);
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
eval(shift(@insns));
|
||||
&vadd_i32 ($T0,$T0,@X[0]);
|
||||
foreach (@insns) { eval; } # remaining instructions
|
||||
&vst1_32 ("{$T0}","[$Xfer,:128]!");
|
||||
|
||||
push(@X,shift(@X)); # "rotate" X[]
|
||||
}
|
||||
|
||||
sub body_00_15 () {
|
||||
(
|
||||
'($a,$b,$c,$d,$e,$f,$g,$h)=@V;'.
|
||||
'&add ($h,$h,$t1)', # h+=X[i]+K[i]
|
||||
'&eor ($t1,$f,$g)',
|
||||
'&eor ($t0,$e,$e,"ror#".($Sigma1[1]-$Sigma1[0]))',
|
||||
'&add ($a,$a,$t2)', # h+=Maj(a,b,c) from the past
|
||||
'&and ($t1,$t1,$e)',
|
||||
'&eor ($t2,$t0,$e,"ror#".($Sigma1[2]-$Sigma1[0]))', # Sigma1(e)
|
||||
'&eor ($t0,$a,$a,"ror#".($Sigma0[1]-$Sigma0[0]))',
|
||||
'&eor ($t1,$t1,$g)', # Ch(e,f,g)
|
||||
'&add ($h,$h,$t2,"ror#$Sigma1[0]")', # h+=Sigma1(e)
|
||||
'&eor ($t2,$a,$b)', # a^b, b^c in next round
|
||||
'&eor ($t0,$t0,$a,"ror#".($Sigma0[2]-$Sigma0[0]))', # Sigma0(a)
|
||||
'&add ($h,$h,$t1)', # h+=Ch(e,f,g)
|
||||
'&ldr ($t1,sprintf "[sp,#%d]",4*(($j+1)&15)) if (($j&15)!=15);'.
|
||||
'&ldr ($t1,"[$Ktbl]") if ($j==15);'.
|
||||
'&ldr ($t1,"[sp,#64]") if ($j==31)',
|
||||
'&and ($t3,$t3,$t2)', # (b^c)&=(a^b)
|
||||
'&add ($d,$d,$h)', # d+=h
|
||||
'&add ($h,$h,$t0,"ror#$Sigma0[0]");'. # h+=Sigma0(a)
|
||||
'&eor ($t3,$t3,$b)', # Maj(a,b,c)
|
||||
'$j++; unshift(@V,pop(@V)); ($t2,$t3)=($t3,$t2);'
|
||||
)
|
||||
}
|
||||
|
||||
$code.=<<___;
|
||||
#if __ARM_MAX_ARCH__>=7
|
||||
.arch armv7-a
|
||||
.fpu neon
|
||||
|
||||
.global sha256_block_data_order_neon
|
||||
.type sha256_block_data_order_neon,%function
|
||||
.align 4
|
||||
sha256_block_data_order_neon:
|
||||
.LNEON:
|
||||
stmdb sp!,{r4-r12,lr}
|
||||
|
||||
sub $H,sp,#16*4+16
|
||||
adrl $Ktbl,K256
|
||||
bic $H,$H,#15 @ align for 128-bit stores
|
||||
mov $t2,sp
|
||||
mov sp,$H @ alloca
|
||||
add $len,$inp,$len,lsl#6 @ len to point at the end of inp
|
||||
|
||||
vld1.8 {@X[0]},[$inp]!
|
||||
vld1.8 {@X[1]},[$inp]!
|
||||
vld1.8 {@X[2]},[$inp]!
|
||||
vld1.8 {@X[3]},[$inp]!
|
||||
vld1.32 {$T0},[$Ktbl,:128]!
|
||||
vld1.32 {$T1},[$Ktbl,:128]!
|
||||
vld1.32 {$T2},[$Ktbl,:128]!
|
||||
vld1.32 {$T3},[$Ktbl,:128]!
|
||||
vrev32.8 @X[0],@X[0] @ yes, even on
|
||||
str $ctx,[sp,#64]
|
||||
vrev32.8 @X[1],@X[1] @ big-endian
|
||||
str $inp,[sp,#68]
|
||||
mov $Xfer,sp
|
||||
vrev32.8 @X[2],@X[2]
|
||||
str $len,[sp,#72]
|
||||
vrev32.8 @X[3],@X[3]
|
||||
str $t2,[sp,#76] @ save original sp
|
||||
vadd.i32 $T0,$T0,@X[0]
|
||||
vadd.i32 $T1,$T1,@X[1]
|
||||
vst1.32 {$T0},[$Xfer,:128]!
|
||||
vadd.i32 $T2,$T2,@X[2]
|
||||
vst1.32 {$T1},[$Xfer,:128]!
|
||||
vadd.i32 $T3,$T3,@X[3]
|
||||
vst1.32 {$T2},[$Xfer,:128]!
|
||||
vst1.32 {$T3},[$Xfer,:128]!
|
||||
|
||||
ldmia $ctx,{$A-$H}
|
||||
sub $Xfer,$Xfer,#64
|
||||
ldr $t1,[sp,#0]
|
||||
eor $t2,$t2,$t2
|
||||
eor $t3,$B,$C
|
||||
b .L_00_48
|
||||
|
||||
.align 4
|
||||
.L_00_48:
|
||||
___
|
||||
&Xupdate(\&body_00_15);
|
||||
&Xupdate(\&body_00_15);
|
||||
&Xupdate(\&body_00_15);
|
||||
&Xupdate(\&body_00_15);
|
||||
$code.=<<___;
|
||||
teq $t1,#0 @ check for K256 terminator
|
||||
ldr $t1,[sp,#0]
|
||||
sub $Xfer,$Xfer,#64
|
||||
bne .L_00_48
|
||||
|
||||
ldr $inp,[sp,#68]
|
||||
ldr $t0,[sp,#72]
|
||||
sub $Ktbl,$Ktbl,#256 @ rewind $Ktbl
|
||||
teq $inp,$t0
|
||||
it eq
|
||||
subeq $inp,$inp,#64 @ avoid SEGV
|
||||
vld1.8 {@X[0]},[$inp]! @ load next input block
|
||||
vld1.8 {@X[1]},[$inp]!
|
||||
vld1.8 {@X[2]},[$inp]!
|
||||
vld1.8 {@X[3]},[$inp]!
|
||||
it ne
|
||||
strne $inp,[sp,#68]
|
||||
mov $Xfer,sp
|
||||
___
|
||||
&Xpreload(\&body_00_15);
|
||||
&Xpreload(\&body_00_15);
|
||||
&Xpreload(\&body_00_15);
|
||||
&Xpreload(\&body_00_15);
|
||||
$code.=<<___;
|
||||
ldr $t0,[$t1,#0]
|
||||
add $A,$A,$t2 @ h+=Maj(a,b,c) from the past
|
||||
ldr $t2,[$t1,#4]
|
||||
ldr $t3,[$t1,#8]
|
||||
ldr $t4,[$t1,#12]
|
||||
add $A,$A,$t0 @ accumulate
|
||||
ldr $t0,[$t1,#16]
|
||||
add $B,$B,$t2
|
||||
ldr $t2,[$t1,#20]
|
||||
add $C,$C,$t3
|
||||
ldr $t3,[$t1,#24]
|
||||
add $D,$D,$t4
|
||||
ldr $t4,[$t1,#28]
|
||||
add $E,$E,$t0
|
||||
str $A,[$t1],#4
|
||||
add $F,$F,$t2
|
||||
str $B,[$t1],#4
|
||||
add $G,$G,$t3
|
||||
str $C,[$t1],#4
|
||||
add $H,$H,$t4
|
||||
str $D,[$t1],#4
|
||||
stmia $t1,{$E-$H}
|
||||
|
||||
ittte ne
|
||||
movne $Xfer,sp
|
||||
ldrne $t1,[sp,#0]
|
||||
eorne $t2,$t2,$t2
|
||||
ldreq sp,[sp,#76] @ restore original sp
|
||||
itt ne
|
||||
eorne $t3,$B,$C
|
||||
bne .L_00_48
|
||||
|
||||
ldmia sp!,{r4-r12,pc}
|
||||
.size sha256_block_data_order_neon,.-sha256_block_data_order_neon
|
||||
#endif
|
||||
___
|
||||
}}}
|
||||
######################################################################
|
||||
# ARMv8 stuff
|
||||
#
|
||||
{{{
|
||||
my ($ABCD,$EFGH,$abcd)=map("q$_",(0..2));
|
||||
my @MSG=map("q$_",(8..11));
|
||||
my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
|
||||
my $Ktbl="r3";
|
||||
|
||||
$code.=<<___;
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
|
||||
# ifdef __thumb2__
|
||||
# define INST(a,b,c,d) .byte c,d|0xc,a,b
|
||||
# else
|
||||
# define INST(a,b,c,d) .byte a,b,c,d
|
||||
# endif
|
||||
|
||||
.type sha256_block_data_order_armv8,%function
|
||||
.align 5
|
||||
sha256_block_data_order_armv8:
|
||||
.LARMv8:
|
||||
vld1.32 {$ABCD,$EFGH},[$ctx]
|
||||
# ifdef __thumb2__
|
||||
adr $Ktbl,.LARMv8
|
||||
sub $Ktbl,$Ktbl,#.LARMv8-K256
|
||||
# else
|
||||
adrl $Ktbl,K256
|
||||
# endif
|
||||
add $len,$inp,$len,lsl#6 @ len to point at the end of inp
|
||||
|
||||
.Loop_v8:
|
||||
vld1.8 {@MSG[0]-@MSG[1]},[$inp]!
|
||||
vld1.8 {@MSG[2]-@MSG[3]},[$inp]!
|
||||
vld1.32 {$W0},[$Ktbl]!
|
||||
vrev32.8 @MSG[0],@MSG[0]
|
||||
vrev32.8 @MSG[1],@MSG[1]
|
||||
vrev32.8 @MSG[2],@MSG[2]
|
||||
vrev32.8 @MSG[3],@MSG[3]
|
||||
vmov $ABCD_SAVE,$ABCD @ offload
|
||||
vmov $EFGH_SAVE,$EFGH
|
||||
teq $inp,$len
|
||||
___
|
||||
for($i=0;$i<12;$i++) {
|
||||
$code.=<<___;
|
||||
vld1.32 {$W1},[$Ktbl]!
|
||||
vadd.i32 $W0,$W0,@MSG[0]
|
||||
sha256su0 @MSG[0],@MSG[1]
|
||||
vmov $abcd,$ABCD
|
||||
sha256h $ABCD,$EFGH,$W0
|
||||
sha256h2 $EFGH,$abcd,$W0
|
||||
sha256su1 @MSG[0],@MSG[2],@MSG[3]
|
||||
___
|
||||
($W0,$W1)=($W1,$W0); push(@MSG,shift(@MSG));
|
||||
}
|
||||
$code.=<<___;
|
||||
vld1.32 {$W1},[$Ktbl]!
|
||||
vadd.i32 $W0,$W0,@MSG[0]
|
||||
vmov $abcd,$ABCD
|
||||
sha256h $ABCD,$EFGH,$W0
|
||||
sha256h2 $EFGH,$abcd,$W0
|
||||
|
||||
vld1.32 {$W0},[$Ktbl]!
|
||||
vadd.i32 $W1,$W1,@MSG[1]
|
||||
vmov $abcd,$ABCD
|
||||
sha256h $ABCD,$EFGH,$W1
|
||||
sha256h2 $EFGH,$abcd,$W1
|
||||
|
||||
vld1.32 {$W1},[$Ktbl]
|
||||
vadd.i32 $W0,$W0,@MSG[2]
|
||||
sub $Ktbl,$Ktbl,#256-16 @ rewind
|
||||
vmov $abcd,$ABCD
|
||||
sha256h $ABCD,$EFGH,$W0
|
||||
sha256h2 $EFGH,$abcd,$W0
|
||||
|
||||
vadd.i32 $W1,$W1,@MSG[3]
|
||||
vmov $abcd,$ABCD
|
||||
sha256h $ABCD,$EFGH,$W1
|
||||
sha256h2 $EFGH,$abcd,$W1
|
||||
|
||||
vadd.i32 $ABCD,$ABCD,$ABCD_SAVE
|
||||
vadd.i32 $EFGH,$EFGH,$EFGH_SAVE
|
||||
it ne
|
||||
bne .Loop_v8
|
||||
|
||||
vst1.32 {$ABCD,$EFGH},[$ctx]
|
||||
|
||||
ret @ bx lr
|
||||
.size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
|
||||
#endif
|
||||
___
|
||||
}}}
|
||||
$code.=<<___;
|
||||
.asciz "SHA256 block transform for ARMv4/NEON/ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
|
||||
.align 2
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
.comm OPENSSL_armcap_P,4,4
|
||||
#endif
|
||||
___
|
||||
|
||||
open SELF,$0;
|
||||
while(<SELF>) {
|
||||
next if (/^#!/);
|
||||
last if (!s/^#/@/ and !/^$/);
|
||||
print;
|
||||
}
|
||||
close SELF;
|
||||
|
||||
{ my %opcode = (
|
||||
"sha256h" => 0xf3000c40, "sha256h2" => 0xf3100c40,
|
||||
"sha256su0" => 0xf3ba03c0, "sha256su1" => 0xf3200c40 );
|
||||
|
||||
sub unsha256 {
|
||||
my ($mnemonic,$arg)=@_;
|
||||
|
||||
if ($arg =~ m/q([0-9]+)(?:,\s*q([0-9]+))?,\s*q([0-9]+)/o) {
|
||||
my $word = $opcode{$mnemonic}|(($1&7)<<13)|(($1&8)<<19)
|
||||
|(($2&7)<<17)|(($2&8)<<4)
|
||||
|(($3&7)<<1) |(($3&8)<<2);
|
||||
# since ARMv7 instructions are always encoded little-endian.
|
||||
# correct solution is to use .inst directive, but older
|
||||
# assemblers don't implement it:-(
|
||||
sprintf "INST(0x%02x,0x%02x,0x%02x,0x%02x)\t@ %s %s",
|
||||
$word&0xff,($word>>8)&0xff,
|
||||
($word>>16)&0xff,($word>>24)&0xff,
|
||||
$mnemonic,$arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (split($/,$code)) {
|
||||
|
||||
s/\`([^\`]*)\`/eval $1/geo;
|
||||
|
||||
s/\b(sha256\w+)\s+(q.*)/unsha256($1,$2)/geo;
|
||||
|
||||
s/\bret\b/bx lr/go or
|
||||
s/\bbx\s+lr\b/.word\t0xe12fff1e/go; # make it possible to compile with -march=armv4
|
||||
|
||||
print $_,"\n";
|
||||
}
|
||||
|
||||
close STDOUT; # enforce flush
|
||||
2808
arch/arm/crypto/sha256-core.S_shipped
Normal file
2808
arch/arm/crypto/sha256-core.S_shipped
Normal file
File diff suppressed because it is too large
Load Diff
246
arch/arm/crypto/sha256_glue.c
Normal file
246
arch/arm/crypto/sha256_glue.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Glue code for the SHA256 Secure Hash Algorithm assembly implementation
|
||||
* using optimized ARM assembler and NEON instructions.
|
||||
*
|
||||
* Copyright © 2015 Google Inc.
|
||||
*
|
||||
* This file is based on sha256_ssse3_glue.c:
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
* Author: Tim Chen <tim.c.chen@linux.intel.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/simd.h>
|
||||
#include <asm/neon.h>
|
||||
#include "sha256_glue.h"
|
||||
|
||||
asmlinkage void sha256_block_data_order(u32 *digest, const void *data,
|
||||
unsigned int num_blks);
|
||||
|
||||
|
||||
int sha256_init(struct shash_desc *desc)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
sctx->state[0] = SHA256_H0;
|
||||
sctx->state[1] = SHA256_H1;
|
||||
sctx->state[2] = SHA256_H2;
|
||||
sctx->state[3] = SHA256_H3;
|
||||
sctx->state[4] = SHA256_H4;
|
||||
sctx->state[5] = SHA256_H5;
|
||||
sctx->state[6] = SHA256_H6;
|
||||
sctx->state[7] = SHA256_H7;
|
||||
sctx->count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha224_init(struct shash_desc *desc)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
sctx->state[0] = SHA224_H0;
|
||||
sctx->state[1] = SHA224_H1;
|
||||
sctx->state[2] = SHA224_H2;
|
||||
sctx->state[3] = SHA224_H3;
|
||||
sctx->state[4] = SHA224_H4;
|
||||
sctx->state[5] = SHA224_H5;
|
||||
sctx->state[6] = SHA224_H6;
|
||||
sctx->state[7] = SHA224_H7;
|
||||
sctx->count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len,
|
||||
unsigned int partial)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int done = 0;
|
||||
|
||||
sctx->count += len;
|
||||
|
||||
if (partial) {
|
||||
done = SHA256_BLOCK_SIZE - partial;
|
||||
memcpy(sctx->buf + partial, data, done);
|
||||
sha256_block_data_order(sctx->state, sctx->buf, 1);
|
||||
}
|
||||
|
||||
if (len - done >= SHA256_BLOCK_SIZE) {
|
||||
const unsigned int rounds = (len - done) / SHA256_BLOCK_SIZE;
|
||||
|
||||
sha256_block_data_order(sctx->state, data + done, rounds);
|
||||
done += rounds * SHA256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
memcpy(sctx->buf, data + done, len - done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
|
||||
|
||||
/* Handle the fast case right here */
|
||||
if (partial + len < SHA256_BLOCK_SIZE) {
|
||||
sctx->count += len;
|
||||
memcpy(sctx->buf + partial, data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return __sha256_update(desc, data, len, partial);
|
||||
}
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
static int sha256_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int i, index, padlen;
|
||||
__be32 *dst = (__be32 *)out;
|
||||
__be64 bits;
|
||||
static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, };
|
||||
|
||||
/* save number of bits */
|
||||
bits = cpu_to_be64(sctx->count << 3);
|
||||
|
||||
/* Pad out to 56 mod 64 and append length */
|
||||
index = sctx->count % SHA256_BLOCK_SIZE;
|
||||
padlen = (index < 56) ? (56 - index) : ((SHA256_BLOCK_SIZE+56)-index);
|
||||
|
||||
/* We need to fill a whole block for __sha256_update */
|
||||
if (padlen <= 56) {
|
||||
sctx->count += padlen;
|
||||
memcpy(sctx->buf + index, padding, padlen);
|
||||
} else {
|
||||
__sha256_update(desc, padding, padlen, index);
|
||||
}
|
||||
__sha256_update(desc, (const u8 *)&bits, sizeof(bits), 56);
|
||||
|
||||
/* Store state in digest */
|
||||
for (i = 0; i < 8; i++)
|
||||
dst[i] = cpu_to_be32(sctx->state[i]);
|
||||
|
||||
/* Wipe context */
|
||||
memset(sctx, 0, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha224_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
u8 D[SHA256_DIGEST_SIZE];
|
||||
|
||||
sha256_final(desc, D);
|
||||
|
||||
memcpy(out, D, SHA224_DIGEST_SIZE);
|
||||
memset(D, 0, SHA256_DIGEST_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha256_export(struct shash_desc *desc, void *out)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
memcpy(out, sctx, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha256_import(struct shash_desc *desc, const void *in)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
memcpy(sctx, in, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct shash_alg algs[] = { {
|
||||
.digestsize = SHA256_DIGEST_SIZE,
|
||||
.init = sha256_init,
|
||||
.update = sha256_update,
|
||||
.final = sha256_final,
|
||||
.export = sha256_export,
|
||||
.import = sha256_import,
|
||||
.descsize = sizeof(struct sha256_state),
|
||||
.statesize = sizeof(struct sha256_state),
|
||||
.base = {
|
||||
.cra_name = "sha256",
|
||||
.cra_driver_name = "sha256-asm",
|
||||
.cra_priority = 150,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA256_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
}, {
|
||||
.digestsize = SHA224_DIGEST_SIZE,
|
||||
.init = sha224_init,
|
||||
.update = sha256_update,
|
||||
.final = sha224_final,
|
||||
.export = sha256_export,
|
||||
.import = sha256_import,
|
||||
.descsize = sizeof(struct sha256_state),
|
||||
.statesize = sizeof(struct sha256_state),
|
||||
.base = {
|
||||
.cra_name = "sha224",
|
||||
.cra_driver_name = "sha224-asm",
|
||||
.cra_priority = 150,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA224_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
} };
|
||||
|
||||
static int __init sha256_mod_init(void)
|
||||
{
|
||||
int res = crypto_register_shashes(algs, ARRAY_SIZE(algs));
|
||||
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) {
|
||||
res = crypto_register_shashes(sha256_neon_algs,
|
||||
ARRAY_SIZE(sha256_neon_algs));
|
||||
|
||||
if (res < 0)
|
||||
crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void __exit sha256_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
|
||||
|
||||
if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon())
|
||||
crypto_unregister_shashes(sha256_neon_algs,
|
||||
ARRAY_SIZE(sha256_neon_algs));
|
||||
}
|
||||
|
||||
module_init(sha256_mod_init);
|
||||
module_exit(sha256_mod_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("SHA256 Secure Hash Algorithm (ARM), including NEON");
|
||||
|
||||
MODULE_ALIAS_CRYPTO("sha256");
|
||||
23
arch/arm/crypto/sha256_glue.h
Normal file
23
arch/arm/crypto/sha256_glue.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _CRYPTO_SHA256_GLUE_H
|
||||
#define _CRYPTO_SHA256_GLUE_H
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/sha.h>
|
||||
|
||||
extern struct shash_alg sha256_neon_algs[2];
|
||||
|
||||
extern int sha256_init(struct shash_desc *desc);
|
||||
|
||||
extern int sha224_init(struct shash_desc *desc);
|
||||
|
||||
extern int __sha256_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, unsigned int partial);
|
||||
|
||||
extern int sha256_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len);
|
||||
|
||||
extern int sha256_export(struct shash_desc *desc, void *out);
|
||||
|
||||
extern int sha256_import(struct shash_desc *desc, const void *in);
|
||||
|
||||
#endif /* _CRYPTO_SHA256_GLUE_H */
|
||||
172
arch/arm/crypto/sha256_neon_glue.c
Normal file
172
arch/arm/crypto/sha256_neon_glue.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Glue code for the SHA256 Secure Hash Algorithm assembly implementation
|
||||
* using NEON instructions.
|
||||
*
|
||||
* Copyright © 2015 Google Inc.
|
||||
*
|
||||
* This file is based on sha512_neon_glue.c:
|
||||
* Copyright © 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/simd.h>
|
||||
#include <asm/neon.h>
|
||||
#include "sha256_glue.h"
|
||||
|
||||
asmlinkage void sha256_block_data_order_neon(u32 *digest, const void *data,
|
||||
unsigned int num_blks);
|
||||
|
||||
|
||||
static int __sha256_neon_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, unsigned int partial)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int done = 0;
|
||||
|
||||
sctx->count += len;
|
||||
|
||||
if (partial) {
|
||||
done = SHA256_BLOCK_SIZE - partial;
|
||||
memcpy(sctx->buf + partial, data, done);
|
||||
sha256_block_data_order_neon(sctx->state, sctx->buf, 1);
|
||||
}
|
||||
|
||||
if (len - done >= SHA256_BLOCK_SIZE) {
|
||||
const unsigned int rounds = (len - done) / SHA256_BLOCK_SIZE;
|
||||
|
||||
sha256_block_data_order_neon(sctx->state, data + done, rounds);
|
||||
done += rounds * SHA256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
memcpy(sctx->buf, data + done, len - done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha256_neon_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
|
||||
int res;
|
||||
|
||||
/* Handle the fast case right here */
|
||||
if (partial + len < SHA256_BLOCK_SIZE) {
|
||||
sctx->count += len;
|
||||
memcpy(sctx->buf + partial, data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!may_use_simd()) {
|
||||
res = __sha256_update(desc, data, len, partial);
|
||||
} else {
|
||||
kernel_neon_begin();
|
||||
res = __sha256_neon_update(desc, data, len, partial);
|
||||
kernel_neon_end();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
static int sha256_neon_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
struct sha256_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int i, index, padlen;
|
||||
__be32 *dst = (__be32 *)out;
|
||||
__be64 bits;
|
||||
static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, };
|
||||
|
||||
/* save number of bits */
|
||||
bits = cpu_to_be64(sctx->count << 3);
|
||||
|
||||
/* Pad out to 56 mod 64 and append length */
|
||||
index = sctx->count % SHA256_BLOCK_SIZE;
|
||||
padlen = (index < 56) ? (56 - index) : ((SHA256_BLOCK_SIZE+56)-index);
|
||||
|
||||
if (!may_use_simd()) {
|
||||
sha256_update(desc, padding, padlen);
|
||||
sha256_update(desc, (const u8 *)&bits, sizeof(bits));
|
||||
} else {
|
||||
kernel_neon_begin();
|
||||
/* We need to fill a whole block for __sha256_neon_update() */
|
||||
if (padlen <= 56) {
|
||||
sctx->count += padlen;
|
||||
memcpy(sctx->buf + index, padding, padlen);
|
||||
} else {
|
||||
__sha256_neon_update(desc, padding, padlen, index);
|
||||
}
|
||||
__sha256_neon_update(desc, (const u8 *)&bits,
|
||||
sizeof(bits), 56);
|
||||
kernel_neon_end();
|
||||
}
|
||||
|
||||
/* Store state in digest */
|
||||
for (i = 0; i < 8; i++)
|
||||
dst[i] = cpu_to_be32(sctx->state[i]);
|
||||
|
||||
/* Wipe context */
|
||||
memset(sctx, 0, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha224_neon_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
u8 D[SHA256_DIGEST_SIZE];
|
||||
|
||||
sha256_neon_final(desc, D);
|
||||
|
||||
memcpy(out, D, SHA224_DIGEST_SIZE);
|
||||
memset(D, 0, SHA256_DIGEST_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct shash_alg sha256_neon_algs[] = { {
|
||||
.digestsize = SHA256_DIGEST_SIZE,
|
||||
.init = sha256_init,
|
||||
.update = sha256_neon_update,
|
||||
.final = sha256_neon_final,
|
||||
.export = sha256_export,
|
||||
.import = sha256_import,
|
||||
.descsize = sizeof(struct sha256_state),
|
||||
.statesize = sizeof(struct sha256_state),
|
||||
.base = {
|
||||
.cra_name = "sha256",
|
||||
.cra_driver_name = "sha256-neon",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA256_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
}, {
|
||||
.digestsize = SHA224_DIGEST_SIZE,
|
||||
.init = sha224_init,
|
||||
.update = sha256_neon_update,
|
||||
.final = sha224_neon_final,
|
||||
.export = sha256_export,
|
||||
.import = sha256_import,
|
||||
.descsize = sizeof(struct sha256_state),
|
||||
.statesize = sizeof(struct sha256_state),
|
||||
.base = {
|
||||
.cra_name = "sha224",
|
||||
.cra_driver_name = "sha224-neon",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA224_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
} };
|
||||
@@ -556,6 +556,13 @@ config CRYPTO_SHA256
|
||||
This code also includes SHA-224, a 224 bit hash with 112 bits
|
||||
of security against collision attacks.
|
||||
|
||||
config CRYPTO_SHA256_ARM
|
||||
tristate "SHA-224/256 digest algorithm (ARM-asm and NEON)"
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
SHA-256 secure hash standard (DFIPS 180-2) implemented
|
||||
using optimized ARM assembler and NEON, when available.
|
||||
|
||||
config CRYPTO_SHA256_SPARC64
|
||||
tristate "SHA224 and SHA256 digest algorithm (SPARC64)"
|
||||
depends on SPARC64
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "power.h"
|
||||
@@ -661,16 +662,31 @@ EXPORT_SYMBOL_GPL(pm_wakeup_event);
|
||||
|
||||
void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
struct wakeup_source *ws, *last_active_ws = NULL;
|
||||
int len = 0;
|
||||
bool active = false;
|
||||
|
||||
rcu_read_lock();
|
||||
len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: ");
|
||||
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
|
||||
if (ws->active) {
|
||||
len += snprintf(pending_wakeup_source + len, max,
|
||||
if (!active)
|
||||
len += scnprintf(pending_wakeup_source, max,
|
||||
"Pending Wakeup Sources: ");
|
||||
len += scnprintf(pending_wakeup_source + len, max - len,
|
||||
"%s ", ws->name);
|
||||
active = true;
|
||||
} else if (!active &&
|
||||
(!last_active_ws ||
|
||||
ktime_to_ns(ws->last_time) >
|
||||
ktime_to_ns(last_active_ws->last_time))) {
|
||||
last_active_ws = ws;
|
||||
}
|
||||
}
|
||||
if (!active && last_active_ws) {
|
||||
scnprintf(pending_wakeup_source, max,
|
||||
"Last active Wakeup Source: %s",
|
||||
last_active_ws->name);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources);
|
||||
|
||||
@@ -531,6 +531,12 @@ config SRAM
|
||||
the genalloc API. It is supposed to be used for small on-chip SRAM
|
||||
areas found on many SoCs.
|
||||
|
||||
config UID_CPUTIME
|
||||
tristate "Per-UID cpu time statistics"
|
||||
depends on PROFILING
|
||||
help
|
||||
Per UID based cpu time statistics exported to /proc/uid_cputime
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
||||
@@ -54,3 +54,4 @@ obj-$(CONFIG_INTEL_MEI) += mei/
|
||||
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
|
||||
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
|
||||
obj-$(CONFIG_SRAM) += sram.o
|
||||
obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
|
||||
|
||||
237
drivers/misc/uid_cputime.c
Normal file
237
drivers/misc/uid_cputime.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/* drivers/misc/uid_cputime.c
|
||||
*
|
||||
* Copyright (C) 2014 - 2015 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define UID_HASH_BITS 10
|
||||
DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
|
||||
|
||||
static DEFINE_MUTEX(uid_lock);
|
||||
static struct proc_dir_entry *parent;
|
||||
|
||||
struct uid_entry {
|
||||
uid_t uid;
|
||||
cputime_t utime;
|
||||
cputime_t stime;
|
||||
cputime_t active_utime;
|
||||
cputime_t active_stime;
|
||||
struct hlist_node hash;
|
||||
};
|
||||
|
||||
static struct uid_entry *find_uid_entry(uid_t uid)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
hash_for_each_possible(hash_table, uid_entry, hash, uid) {
|
||||
if (uid_entry->uid == uid)
|
||||
return uid_entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct uid_entry *find_or_register_uid(uid_t uid)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
|
||||
uid_entry = find_uid_entry(uid);
|
||||
if (uid_entry)
|
||||
return uid_entry;
|
||||
|
||||
uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
|
||||
if (!uid_entry)
|
||||
return NULL;
|
||||
|
||||
uid_entry->uid = uid;
|
||||
|
||||
hash_add(hash_table, &uid_entry->hash, uid);
|
||||
|
||||
return uid_entry;
|
||||
}
|
||||
|
||||
static int uid_stat_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
struct task_struct *task;
|
||||
cputime_t utime;
|
||||
cputime_t stime;
|
||||
unsigned long bkt;
|
||||
|
||||
mutex_lock(&uid_lock);
|
||||
|
||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
||||
uid_entry->active_stime = 0;
|
||||
uid_entry->active_utime = 0;
|
||||
}
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
for_each_process(task) {
|
||||
uid_entry = find_or_register_uid(from_kuid_munged(
|
||||
current_user_ns(), task_uid(task)));
|
||||
if (!uid_entry) {
|
||||
read_unlock(&tasklist_lock);
|
||||
mutex_unlock(&uid_lock);
|
||||
pr_err("%s: failed to find the uid_entry for uid %d\n",
|
||||
__func__, from_kuid_munged(current_user_ns(),
|
||||
task_uid(task)));
|
||||
return -ENOMEM;
|
||||
}
|
||||
task_cputime_adjusted(task, &utime, &stime);
|
||||
uid_entry->active_utime += utime;
|
||||
uid_entry->active_stime += stime;
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
hash_for_each(hash_table, bkt, uid_entry, hash) {
|
||||
cputime_t total_utime = uid_entry->utime +
|
||||
uid_entry->active_utime;
|
||||
cputime_t total_stime = uid_entry->stime +
|
||||
uid_entry->active_stime;
|
||||
seq_printf(m, "%d: %u %u\n", uid_entry->uid,
|
||||
cputime_to_usecs(total_utime),
|
||||
cputime_to_usecs(total_stime));
|
||||
}
|
||||
|
||||
mutex_unlock(&uid_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uid_stat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uid_stat_show, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations uid_stat_fops = {
|
||||
.open = uid_stat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int uid_remove_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, NULL, NULL);
|
||||
}
|
||||
|
||||
static ssize_t uid_remove_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct uid_entry *uid_entry;
|
||||
struct hlist_node *tmp;
|
||||
char uids[128];
|
||||
char *start_uid, *end_uid = NULL;
|
||||
long int uid_start = 0, uid_end = 0;
|
||||
|
||||
if (count >= sizeof(uids))
|
||||
count = sizeof(uids) - 1;
|
||||
|
||||
if (copy_from_user(uids, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
uids[count] = '\0';
|
||||
end_uid = uids;
|
||||
start_uid = strsep(&end_uid, "-");
|
||||
|
||||
if (!start_uid || !end_uid)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtol(start_uid, 10, &uid_start) != 0 ||
|
||||
kstrtol(end_uid, 10, &uid_end) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&uid_lock);
|
||||
|
||||
for (; uid_start <= uid_end; uid_start++) {
|
||||
hash_for_each_possible_safe(hash_table, uid_entry, tmp,
|
||||
hash, uid_start) {
|
||||
hash_del(&uid_entry->hash);
|
||||
kfree(uid_entry);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&uid_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations uid_remove_fops = {
|
||||
.open = uid_remove_open,
|
||||
.release = single_release,
|
||||
.write = uid_remove_write,
|
||||
};
|
||||
|
||||
static int process_notifier(struct notifier_block *self,
|
||||
unsigned long cmd, void *v)
|
||||
{
|
||||
struct task_struct *task = v;
|
||||
struct uid_entry *uid_entry;
|
||||
cputime_t utime, stime;
|
||||
uid_t uid;
|
||||
|
||||
if (!task)
|
||||
return NOTIFY_OK;
|
||||
|
||||
mutex_lock(&uid_lock);
|
||||
uid = from_kuid_munged(current_user_ns(), task_uid(task));
|
||||
uid_entry = find_or_register_uid(uid);
|
||||
if (!uid_entry) {
|
||||
pr_err("%s: failed to find uid %d\n", __func__, uid);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
task_cputime_adjusted(task, &utime, &stime);
|
||||
uid_entry->utime += utime;
|
||||
uid_entry->stime += stime;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&uid_lock);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block process_notifier_block = {
|
||||
.notifier_call = process_notifier,
|
||||
};
|
||||
|
||||
static int __init proc_uid_cputime_init(void)
|
||||
{
|
||||
hash_init(hash_table);
|
||||
|
||||
parent = proc_mkdir("uid_cputime", NULL);
|
||||
if (!parent) {
|
||||
pr_err("%s: failed to create proc entry\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops,
|
||||
NULL);
|
||||
|
||||
proc_create_data("show_uid_stat", S_IWUGO, parent, &uid_stat_fops,
|
||||
NULL);
|
||||
|
||||
profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_initcall(proc_uid_cputime_init);
|
||||
@@ -20,23 +20,6 @@ config ASHMEM
|
||||
It is, in theory, a good memory allocator for low-memory devices,
|
||||
because it can discard shared memory units when under memory pressure.
|
||||
|
||||
config ANDROID_LOGGER
|
||||
tristate "Android log driver"
|
||||
default n
|
||||
---help---
|
||||
This adds support for system-wide logging using four log buffers.
|
||||
|
||||
These are:
|
||||
|
||||
1: main
|
||||
2: events
|
||||
3: radio
|
||||
4: system
|
||||
|
||||
Log reading and writing is performed via normal Linux reads and
|
||||
optimized writes. This optimization avoids logging having too
|
||||
much overhead in the system.
|
||||
|
||||
config ANDROID_TIMED_OUTPUT
|
||||
bool "Timed output class driver"
|
||||
default y
|
||||
@@ -61,15 +44,6 @@ config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
|
||||
/sys/module/lowmemorykiller/parameters/adj and convert them
|
||||
to oom_score_adj values.
|
||||
|
||||
config ANDROID_INTF_ALARM_DEV
|
||||
bool "Android alarm driver"
|
||||
depends on RTC_CLASS
|
||||
default n
|
||||
---help---
|
||||
Provides non-wakeup and rtc backed wakeup alarms based on rtc or
|
||||
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
|
||||
Also exports the alarm interface to user-space.
|
||||
|
||||
config SYNC
|
||||
bool "Synchronization framework"
|
||||
default n
|
||||
|
||||
@@ -4,10 +4,8 @@ obj-y += ion/
|
||||
obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/
|
||||
|
||||
obj-$(CONFIG_ASHMEM) += ashmem.o
|
||||
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
|
||||
obj-$(CONFIG_SYNC) += sync.o
|
||||
obj-$(CONFIG_SW_SYNC) += sw_sync.o
|
||||
|
||||
@@ -1,444 +0,0 @@
|
||||
/* drivers/rtc/alarm-dev.c
|
||||
*
|
||||
* Copyright (C) 2007-2009 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/alarmtimer.h>
|
||||
#include "android_alarm.h"
|
||||
|
||||
#define ANDROID_ALARM_PRINT_INFO (1U << 0)
|
||||
#define ANDROID_ALARM_PRINT_IO (1U << 1)
|
||||
#define ANDROID_ALARM_PRINT_INT (1U << 2)
|
||||
|
||||
static int debug_mask = ANDROID_ALARM_PRINT_INFO;
|
||||
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
||||
#define alarm_dbg(debug_level_mask, fmt, ...) \
|
||||
do { \
|
||||
if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) \
|
||||
pr_info(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ANDROID_ALARM_WAKEUP_MASK ( \
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK | \
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
|
||||
|
||||
static int alarm_opened;
|
||||
static DEFINE_SPINLOCK(alarm_slock);
|
||||
static struct wakeup_source alarm_wake_lock;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
|
||||
static uint32_t alarm_pending;
|
||||
static uint32_t alarm_enabled;
|
||||
static uint32_t wait_pending;
|
||||
|
||||
struct devalarm {
|
||||
union {
|
||||
struct hrtimer hrt;
|
||||
struct alarm alrm;
|
||||
} u;
|
||||
enum android_alarm_type type;
|
||||
};
|
||||
|
||||
static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
|
||||
|
||||
|
||||
static int is_wakeup(enum android_alarm_type type)
|
||||
{
|
||||
return (type == ANDROID_ALARM_RTC_WAKEUP ||
|
||||
type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
|
||||
}
|
||||
|
||||
|
||||
static void devalarm_start(struct devalarm *alrm, ktime_t exp)
|
||||
{
|
||||
if (is_wakeup(alrm->type))
|
||||
alarm_start(&alrm->u.alrm, exp);
|
||||
else
|
||||
hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
||||
|
||||
static int devalarm_try_to_cancel(struct devalarm *alrm)
|
||||
{
|
||||
if (is_wakeup(alrm->type))
|
||||
return alarm_try_to_cancel(&alrm->u.alrm);
|
||||
return hrtimer_try_to_cancel(&alrm->u.hrt);
|
||||
}
|
||||
|
||||
static void devalarm_cancel(struct devalarm *alrm)
|
||||
{
|
||||
if (is_wakeup(alrm->type))
|
||||
alarm_cancel(&alrm->u.alrm);
|
||||
else
|
||||
hrtimer_cancel(&alrm->u.hrt);
|
||||
}
|
||||
|
||||
static void alarm_clear(enum android_alarm_type alarm_type)
|
||||
{
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d clear\n", alarm_type);
|
||||
devalarm_try_to_cancel(&alarms[alarm_type]);
|
||||
if (alarm_pending) {
|
||||
alarm_pending &= ~alarm_type_mask;
|
||||
if (!alarm_pending && !wait_pending)
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
}
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void alarm_set(enum android_alarm_type alarm_type,
|
||||
struct timespec *ts)
|
||||
{
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
|
||||
alarm_type, ts->tv_sec, ts->tv_nsec);
|
||||
alarm_enabled |= alarm_type_mask;
|
||||
devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
|
||||
static int alarm_wait(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rv = 0;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm wait\n");
|
||||
if (!alarm_pending && wait_pending) {
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
rv = alarm_pending;
|
||||
wait_pending = 1;
|
||||
alarm_pending = 0;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alarm_set_rtc(struct timespec *ts)
|
||||
{
|
||||
struct rtc_time new_rtc_tm;
|
||||
struct rtc_device *rtc_dev;
|
||||
unsigned long flags;
|
||||
int rv = 0;
|
||||
|
||||
rtc_time_to_tm(ts->tv_sec, &new_rtc_tm);
|
||||
rtc_dev = alarmtimer_get_rtcdev();
|
||||
rv = do_settimeofday(ts);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
if (rtc_dev)
|
||||
rv = rtc_set_time(rtc_dev, &new_rtc_tm);
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
|
||||
wake_up(&alarm_wait_queue);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alarm_get_time(enum android_alarm_type alarm_type,
|
||||
struct timespec *ts)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
switch (alarm_type) {
|
||||
case ANDROID_ALARM_RTC_WAKEUP:
|
||||
case ANDROID_ALARM_RTC:
|
||||
getnstimeofday(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME:
|
||||
get_monotonic_boottime(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_SYSTEMTIME:
|
||||
ktime_get_ts(ts);
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long alarm_do_ioctl(struct file *file, unsigned int cmd,
|
||||
struct timespec *ts)
|
||||
{
|
||||
int rv = 0;
|
||||
unsigned long flags;
|
||||
enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
|
||||
|
||||
if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
|
||||
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
|
||||
return -EPERM;
|
||||
if (file->private_data == NULL &&
|
||||
cmd != ANDROID_ALARM_SET_RTC) {
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (alarm_opened) {
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
alarm_opened = 1;
|
||||
file->private_data = (void *)1;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_CLEAR(0):
|
||||
alarm_clear(alarm_type);
|
||||
break;
|
||||
case ANDROID_ALARM_SET(0):
|
||||
alarm_set(alarm_type, ts);
|
||||
break;
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
alarm_set(alarm_type, ts);
|
||||
/* fall though */
|
||||
case ANDROID_ALARM_WAIT:
|
||||
rv = alarm_wait();
|
||||
break;
|
||||
case ANDROID_ALARM_SET_RTC:
|
||||
rv = alarm_set_rtc(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_GET_TIME(0):
|
||||
rv = alarm_get_time(alarm_type, ts);
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
case ANDROID_ALARM_SET(0):
|
||||
case ANDROID_ALARM_SET_RTC:
|
||||
if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = alarm_do_ioctl(file, cmd, &ts);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_GET_TIME(0):
|
||||
if (copy_to_user((void __user *)arg, &ts, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long alarm_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0):
|
||||
case ANDROID_ALARM_SET_COMPAT(0):
|
||||
case ANDROID_ALARM_SET_RTC_COMPAT:
|
||||
if (compat_get_timespec(&ts, (void __user *)arg))
|
||||
return -EFAULT;
|
||||
/* fall through */
|
||||
case ANDROID_ALARM_GET_TIME_COMPAT(0):
|
||||
cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
rv = alarm_do_ioctl(file, cmd, &ts);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */
|
||||
if (compat_put_timespec(&ts, (void __user *)arg))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int alarm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alarm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (file->private_data) {
|
||||
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
|
||||
uint32_t alarm_type_mask = 1U << i;
|
||||
|
||||
if (alarm_enabled & alarm_type_mask) {
|
||||
alarm_dbg(INFO,
|
||||
"%s: clear alarm, pending %d\n",
|
||||
__func__,
|
||||
!!(alarm_pending & alarm_type_mask));
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
devalarm_cancel(&alarms[i]);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
}
|
||||
if (alarm_pending | wait_pending) {
|
||||
if (alarm_pending)
|
||||
alarm_dbg(INFO, "%s: clear pending alarms %x\n",
|
||||
__func__, alarm_pending);
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
alarm_pending = 0;
|
||||
}
|
||||
alarm_opened = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void devalarm_triggered(struct devalarm *alarm)
|
||||
{
|
||||
unsigned long flags;
|
||||
uint32_t alarm_type_mask = 1U << alarm->type;
|
||||
|
||||
alarm_dbg(INT, "%s: type %d\n", __func__, alarm->type);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (alarm_enabled & alarm_type_mask) {
|
||||
__pm_wakeup_event(&alarm_wake_lock, 5000); /* 5secs */
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
alarm_pending |= alarm_type_mask;
|
||||
wake_up(&alarm_wait_queue);
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
|
||||
|
||||
static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt)
|
||||
{
|
||||
struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt);
|
||||
|
||||
devalarm_triggered(devalrm);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm,
|
||||
ktime_t now)
|
||||
{
|
||||
struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm);
|
||||
|
||||
devalarm_triggered(devalrm);
|
||||
return ALARMTIMER_NORESTART;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations alarm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = alarm_ioctl,
|
||||
.open = alarm_open,
|
||||
.release = alarm_release,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = alarm_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct miscdevice alarm_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "alarm",
|
||||
.fops = &alarm_fops,
|
||||
};
|
||||
|
||||
static int __init alarm_dev_init(void)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
|
||||
err = misc_register(&alarm_device);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm,
|
||||
ALARM_REALTIME, devalarm_alarmhandler);
|
||||
hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt,
|
||||
CLOCK_REALTIME, HRTIMER_MODE_ABS);
|
||||
alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm,
|
||||
ALARM_BOOTTIME, devalarm_alarmhandler);
|
||||
hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt,
|
||||
CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
|
||||
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
|
||||
alarms[i].type = i;
|
||||
if (!is_wakeup(i))
|
||||
alarms[i].u.hrt.function = devalarm_hrthandler;
|
||||
}
|
||||
|
||||
wakeup_source_init(&alarm_wake_lock, "alarm");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alarm_dev_exit(void)
|
||||
{
|
||||
misc_deregister(&alarm_device);
|
||||
wakeup_source_trash(&alarm_wake_lock);
|
||||
}
|
||||
|
||||
module_init(alarm_dev_init);
|
||||
module_exit(alarm_dev_exit);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/* include/linux/android_alarm.h
|
||||
*
|
||||
* Copyright (C) 2006-2007 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ANDROID_ALARM_H
|
||||
#define _LINUX_ANDROID_ALARM_H
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#include "uapi/android_alarm.h"
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define ANDROID_ALARM_SET_COMPAT(type) ALARM_IOW(2, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_SET_AND_WAIT_COMPAT(type) ALARM_IOW(3, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_GET_TIME_COMPAT(type) ALARM_IOW(4, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_SET_RTC_COMPAT _IOW('a', 5, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_IOCTL_NR(cmd) (_IOC_NR(cmd) & ((1<<4)-1))
|
||||
#define ANDROID_ALARM_COMPAT_TO_NORM(cmd) \
|
||||
ALARM_IOW(ANDROID_ALARM_IOCTL_NR(cmd), \
|
||||
ANDROID_ALARM_IOCTL_TO_TYPE(cmd), \
|
||||
struct timespec)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,852 +0,0 @@
|
||||
/*
|
||||
* drivers/misc/logger.c
|
||||
*
|
||||
* A Logging Subsystem
|
||||
*
|
||||
* Copyright (C) 2007-2008 Google, Inc.
|
||||
*
|
||||
* Robert Love <rlove@google.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "logger: " fmt
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/aio.h>
|
||||
#include "logger.h"
|
||||
|
||||
#include <asm/ioctls.h>
|
||||
|
||||
/**
|
||||
* struct logger_log - represents a specific log, such as 'main' or 'radio'
|
||||
* @buffer: The actual ring buffer
|
||||
* @misc: The "misc" device representing the log
|
||||
* @wq: The wait queue for @readers
|
||||
* @readers: This log's readers
|
||||
* @mutex: The mutex that protects the @buffer
|
||||
* @w_off: The current write head offset
|
||||
* @head: The head, or location that readers start reading at.
|
||||
* @size: The size of the log
|
||||
* @logs: The list of log channels
|
||||
*
|
||||
* This structure lives from module insertion until module removal, so it does
|
||||
* not need additional reference counting. The structure is protected by the
|
||||
* mutex 'mutex'.
|
||||
*/
|
||||
struct logger_log {
|
||||
unsigned char *buffer;
|
||||
struct miscdevice misc;
|
||||
wait_queue_head_t wq;
|
||||
struct list_head readers;
|
||||
struct mutex mutex;
|
||||
size_t w_off;
|
||||
size_t head;
|
||||
size_t size;
|
||||
struct list_head logs;
|
||||
};
|
||||
|
||||
static LIST_HEAD(log_list);
|
||||
|
||||
|
||||
/**
|
||||
* struct logger_reader - a logging device open for reading
|
||||
* @log: The associated log
|
||||
* @list: The associated entry in @logger_log's list
|
||||
* @r_off: The current read head offset.
|
||||
* @r_all: Reader can read all entries
|
||||
* @r_ver: Reader ABI version
|
||||
*
|
||||
* This object lives from open to release, so we don't need additional
|
||||
* reference counting. The structure is protected by log->mutex.
|
||||
*/
|
||||
struct logger_reader {
|
||||
struct logger_log *log;
|
||||
struct list_head list;
|
||||
size_t r_off;
|
||||
bool r_all;
|
||||
int r_ver;
|
||||
};
|
||||
|
||||
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
|
||||
static size_t logger_offset(struct logger_log *log, size_t n)
|
||||
{
|
||||
return n & (log->size - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* file_get_log - Given a file structure, return the associated log
|
||||
*
|
||||
* This isn't aesthetic. We have several goals:
|
||||
*
|
||||
* 1) Need to quickly obtain the associated log during an I/O operation
|
||||
* 2) Readers need to maintain state (logger_reader)
|
||||
* 3) Writers need to be very fast (open() should be a near no-op)
|
||||
*
|
||||
* In the reader case, we can trivially go file->logger_reader->logger_log.
|
||||
* For a writer, we don't want to maintain a logger_reader, so we just go
|
||||
* file->logger_log. Thus what file->private_data points at depends on whether
|
||||
* or not the file was opened for reading. This function hides that dirtiness.
|
||||
*/
|
||||
static inline struct logger_log *file_get_log(struct file *file)
|
||||
{
|
||||
if (file->f_mode & FMODE_READ) {
|
||||
struct logger_reader *reader = file->private_data;
|
||||
|
||||
return reader->log;
|
||||
} else
|
||||
return file->private_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_entry_header - returns a pointer to the logger_entry header within
|
||||
* 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must
|
||||
* be provided. Typically the return value will be a pointer within
|
||||
* 'logger->buf'. However, a pointer to 'scratch' may be returned if
|
||||
* the log entry spans the end and beginning of the circular buffer.
|
||||
*/
|
||||
static struct logger_entry *get_entry_header(struct logger_log *log,
|
||||
size_t off, struct logger_entry *scratch)
|
||||
{
|
||||
size_t len = min(sizeof(struct logger_entry), log->size - off);
|
||||
|
||||
if (len != sizeof(struct logger_entry)) {
|
||||
memcpy(((void *) scratch), log->buffer + off, len);
|
||||
memcpy(((void *) scratch) + len, log->buffer,
|
||||
sizeof(struct logger_entry) - len);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
return (struct logger_entry *) (log->buffer + off);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_entry_msg_len - Grabs the length of the message of the entry
|
||||
* starting from from 'off'.
|
||||
*
|
||||
* An entry length is 2 bytes (16 bits) in host endian order.
|
||||
* In the log, the length does not include the size of the log entry structure.
|
||||
* This function returns the size including the log entry structure.
|
||||
*
|
||||
* Caller needs to hold log->mutex.
|
||||
*/
|
||||
static __u32 get_entry_msg_len(struct logger_log *log, size_t off)
|
||||
{
|
||||
struct logger_entry scratch;
|
||||
struct logger_entry *entry;
|
||||
|
||||
entry = get_entry_header(log, off, &scratch);
|
||||
return entry->len;
|
||||
}
|
||||
|
||||
static size_t get_user_hdr_len(int ver)
|
||||
{
|
||||
if (ver < 2)
|
||||
return sizeof(struct user_logger_entry_compat);
|
||||
else
|
||||
return sizeof(struct logger_entry);
|
||||
}
|
||||
|
||||
static ssize_t copy_header_to_user(int ver, struct logger_entry *entry,
|
||||
char __user *buf)
|
||||
{
|
||||
void *hdr;
|
||||
size_t hdr_len;
|
||||
struct user_logger_entry_compat v1;
|
||||
|
||||
if (ver < 2) {
|
||||
v1.len = entry->len;
|
||||
v1.__pad = 0;
|
||||
v1.pid = entry->pid;
|
||||
v1.tid = entry->tid;
|
||||
v1.sec = entry->sec;
|
||||
v1.nsec = entry->nsec;
|
||||
hdr = &v1;
|
||||
hdr_len = sizeof(struct user_logger_entry_compat);
|
||||
} else {
|
||||
hdr = entry;
|
||||
hdr_len = sizeof(struct logger_entry);
|
||||
}
|
||||
|
||||
return copy_to_user(buf, hdr, hdr_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* do_read_log_to_user - reads exactly 'count' bytes from 'log' into the
|
||||
* user-space buffer 'buf'. Returns 'count' on success.
|
||||
*
|
||||
* Caller must hold log->mutex.
|
||||
*/
|
||||
static ssize_t do_read_log_to_user(struct logger_log *log,
|
||||
struct logger_reader *reader,
|
||||
char __user *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct logger_entry scratch;
|
||||
struct logger_entry *entry;
|
||||
size_t len;
|
||||
size_t msg_start;
|
||||
|
||||
/*
|
||||
* First, copy the header to userspace, using the version of
|
||||
* the header requested
|
||||
*/
|
||||
entry = get_entry_header(log, reader->r_off, &scratch);
|
||||
if (copy_header_to_user(reader->r_ver, entry, buf))
|
||||
return -EFAULT;
|
||||
|
||||
count -= get_user_hdr_len(reader->r_ver);
|
||||
buf += get_user_hdr_len(reader->r_ver);
|
||||
msg_start = logger_offset(log,
|
||||
reader->r_off + sizeof(struct logger_entry));
|
||||
|
||||
/*
|
||||
* We read from the msg in two disjoint operations. First, we read from
|
||||
* the current msg head offset up to 'count' bytes or to the end of
|
||||
* the log, whichever comes first.
|
||||
*/
|
||||
len = min(count, log->size - msg_start);
|
||||
if (copy_to_user(buf, log->buffer + msg_start, len))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Second, we read any remaining bytes, starting back at the head of
|
||||
* the log.
|
||||
*/
|
||||
if (count != len)
|
||||
if (copy_to_user(buf + len, log->buffer, count - len))
|
||||
return -EFAULT;
|
||||
|
||||
reader->r_off = logger_offset(log, reader->r_off +
|
||||
sizeof(struct logger_entry) + count);
|
||||
|
||||
return count + get_user_hdr_len(reader->r_ver);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_next_entry_by_uid - Starting at 'off', returns an offset into
|
||||
* 'log->buffer' which contains the first entry readable by 'euid'
|
||||
*/
|
||||
static size_t get_next_entry_by_uid(struct logger_log *log,
|
||||
size_t off, kuid_t euid)
|
||||
{
|
||||
while (off != log->w_off) {
|
||||
struct logger_entry *entry;
|
||||
struct logger_entry scratch;
|
||||
size_t next_len;
|
||||
|
||||
entry = get_entry_header(log, off, &scratch);
|
||||
|
||||
if (uid_eq(entry->euid, euid))
|
||||
return off;
|
||||
|
||||
next_len = sizeof(struct logger_entry) + entry->len;
|
||||
off = logger_offset(log, off + next_len);
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
/*
|
||||
* logger_read - our log's read() method
|
||||
*
|
||||
* Behavior:
|
||||
*
|
||||
* - O_NONBLOCK works
|
||||
* - If there are no log entries to read, blocks until log is written to
|
||||
* - Atomically reads exactly one log entry
|
||||
*
|
||||
* Will set errno to EINVAL if read
|
||||
* buffer is insufficient to hold next entry.
|
||||
*/
|
||||
static ssize_t logger_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct logger_reader *reader = file->private_data;
|
||||
struct logger_log *log = reader->log;
|
||||
ssize_t ret;
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
start:
|
||||
while (1) {
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
ret = (log->w_off == reader->r_off);
|
||||
mutex_unlock(&log->mutex);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
schedule();
|
||||
}
|
||||
|
||||
finish_wait(&log->wq, &wait);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
/* is there still something to read or did we race? */
|
||||
if (unlikely(log->w_off == reader->r_off)) {
|
||||
mutex_unlock(&log->mutex);
|
||||
goto start;
|
||||
}
|
||||
|
||||
/* get the size of the next entry */
|
||||
ret = get_user_hdr_len(reader->r_ver) +
|
||||
get_entry_msg_len(log, reader->r_off);
|
||||
if (count < ret) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get exactly one entry from the log */
|
||||
ret = do_read_log_to_user(log, reader, buf, ret);
|
||||
|
||||
out:
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_next_entry - return the offset of the first valid entry at least 'len'
|
||||
* bytes after 'off'.
|
||||
*
|
||||
* Caller must hold log->mutex.
|
||||
*/
|
||||
static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
do {
|
||||
size_t nr = sizeof(struct logger_entry) +
|
||||
get_entry_msg_len(log, off);
|
||||
off = logger_offset(log, off + nr);
|
||||
count += nr;
|
||||
} while (count < len);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_between - is a < c < b, accounting for wrapping of a, b, and c
|
||||
* positions in the buffer
|
||||
*
|
||||
* That is, if a<b, check for c between a and b
|
||||
* and if a>b, check for c outside (not between) a and b
|
||||
*
|
||||
* |------- a xxxxxxxx b --------|
|
||||
* c^
|
||||
*
|
||||
* |xxxxx b --------- a xxxxxxxxx|
|
||||
* c^
|
||||
* or c^
|
||||
*/
|
||||
static inline int is_between(size_t a, size_t b, size_t c)
|
||||
{
|
||||
if (a < b) {
|
||||
/* is c between a and b? */
|
||||
if (a < c && c <= b)
|
||||
return 1;
|
||||
} else {
|
||||
/* is c outside of b through a? */
|
||||
if (c <= b || a < c)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fix_up_readers - walk the list of all readers and "fix up" any who were
|
||||
* lapped by the writer; also do the same for the default "start head".
|
||||
* We do this by "pulling forward" the readers and start head to the first
|
||||
* entry after the new write head.
|
||||
*
|
||||
* The caller needs to hold log->mutex.
|
||||
*/
|
||||
static void fix_up_readers(struct logger_log *log, size_t len)
|
||||
{
|
||||
size_t old = log->w_off;
|
||||
size_t new = logger_offset(log, old + len);
|
||||
struct logger_reader *reader;
|
||||
|
||||
if (is_between(old, new, log->head))
|
||||
log->head = get_next_entry(log, log->head, len);
|
||||
|
||||
list_for_each_entry(reader, &log->readers, list)
|
||||
if (is_between(old, new, reader->r_off))
|
||||
reader->r_off = get_next_entry(log, reader->r_off, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* do_write_log - writes 'len' bytes from 'buf' to 'log'
|
||||
*
|
||||
* The caller needs to hold log->mutex.
|
||||
*/
|
||||
static void do_write_log(struct logger_log *log, const void *buf, size_t count)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = min(count, log->size - log->w_off);
|
||||
memcpy(log->buffer + log->w_off, buf, len);
|
||||
|
||||
if (count != len)
|
||||
memcpy(log->buffer, buf + len, count - len);
|
||||
|
||||
log->w_off = logger_offset(log, log->w_off + count);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to
|
||||
* the log 'log'
|
||||
*
|
||||
* The caller needs to hold log->mutex.
|
||||
*
|
||||
* Returns 'count' on success, negative error code on failure.
|
||||
*/
|
||||
static ssize_t do_write_log_from_user(struct logger_log *log,
|
||||
const void __user *buf, size_t count)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = min(count, log->size - log->w_off);
|
||||
if (len && copy_from_user(log->buffer + log->w_off, buf, len))
|
||||
return -EFAULT;
|
||||
|
||||
if (count != len)
|
||||
if (copy_from_user(log->buffer, buf + len, count - len))
|
||||
/*
|
||||
* Note that by not updating w_off, this abandons the
|
||||
* portion of the new entry that *was* successfully
|
||||
* copied, just above. This is intentional to avoid
|
||||
* message corruption from missing fragments.
|
||||
*/
|
||||
return -EFAULT;
|
||||
|
||||
log->w_off = logger_offset(log, log->w_off + count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* logger_aio_write - our write method, implementing support for write(),
|
||||
* writev(), and aio_write(). Writes are our fast path, and we try to optimize
|
||||
* them above all else.
|
||||
*/
|
||||
static ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t ppos)
|
||||
{
|
||||
struct logger_log *log = file_get_log(iocb->ki_filp);
|
||||
size_t orig = log->w_off;
|
||||
struct logger_entry header;
|
||||
struct timespec now;
|
||||
ssize_t ret = 0;
|
||||
|
||||
now = current_kernel_time();
|
||||
|
||||
header.pid = current->tgid;
|
||||
header.tid = current->pid;
|
||||
header.sec = now.tv_sec;
|
||||
header.nsec = now.tv_nsec;
|
||||
header.euid = current_euid();
|
||||
header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
|
||||
header.hdr_size = sizeof(struct logger_entry);
|
||||
|
||||
/* null writes succeed, return zero */
|
||||
if (unlikely(!header.len))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
/*
|
||||
* Fix up any readers, pulling them forward to the first readable
|
||||
* entry after (what will be) the new write offset. We do this now
|
||||
* because if we partially fail, we can end up with clobbered log
|
||||
* entries that encroach on readable buffer.
|
||||
*/
|
||||
fix_up_readers(log, sizeof(struct logger_entry) + header.len);
|
||||
|
||||
do_write_log(log, &header, sizeof(struct logger_entry));
|
||||
|
||||
while (nr_segs-- > 0) {
|
||||
size_t len;
|
||||
ssize_t nr;
|
||||
|
||||
/* figure out how much of this vector we can keep */
|
||||
len = min_t(size_t, iov->iov_len, header.len - ret);
|
||||
|
||||
/* write out this segment's payload */
|
||||
nr = do_write_log_from_user(log, iov->iov_base, len);
|
||||
if (unlikely(nr < 0)) {
|
||||
log->w_off = orig;
|
||||
mutex_unlock(&log->mutex);
|
||||
return nr;
|
||||
}
|
||||
|
||||
iov++;
|
||||
ret += nr;
|
||||
}
|
||||
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
/* wake up any blocked readers */
|
||||
wake_up_interruptible(&log->wq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct logger_log *get_log_from_minor(int minor)
|
||||
{
|
||||
struct logger_log *log;
|
||||
|
||||
list_for_each_entry(log, &log_list, logs)
|
||||
if (log->misc.minor == minor)
|
||||
return log;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* logger_open - the log's open() file operation
|
||||
*
|
||||
* Note how near a no-op this is in the write-only case. Keep it that way!
|
||||
*/
|
||||
static int logger_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct logger_log *log;
|
||||
int ret;
|
||||
|
||||
ret = nonseekable_open(inode, file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
log = get_log_from_minor(MINOR(inode->i_rdev));
|
||||
if (!log)
|
||||
return -ENODEV;
|
||||
|
||||
if (file->f_mode & FMODE_READ) {
|
||||
struct logger_reader *reader;
|
||||
|
||||
reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
|
||||
if (!reader)
|
||||
return -ENOMEM;
|
||||
|
||||
reader->log = log;
|
||||
reader->r_ver = 1;
|
||||
reader->r_all = in_egroup_p(inode->i_gid) ||
|
||||
capable(CAP_SYSLOG);
|
||||
|
||||
INIT_LIST_HEAD(&reader->list);
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
reader->r_off = log->head;
|
||||
list_add_tail(&reader->list, &log->readers);
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
file->private_data = reader;
|
||||
} else
|
||||
file->private_data = log;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* logger_release - the log's release file operation
|
||||
*
|
||||
* Note this is a total no-op in the write-only case. Keep it that way!
|
||||
*/
|
||||
static int logger_release(struct inode *ignored, struct file *file)
|
||||
{
|
||||
if (file->f_mode & FMODE_READ) {
|
||||
struct logger_reader *reader = file->private_data;
|
||||
struct logger_log *log = reader->log;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
list_del(&reader->list);
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
kfree(reader);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* logger_poll - the log's poll file operation, for poll/select/epoll
|
||||
*
|
||||
* Note we always return POLLOUT, because you can always write() to the log.
|
||||
* Note also that, strictly speaking, a return value of POLLIN does not
|
||||
* guarantee that the log is readable without blocking, as there is a small
|
||||
* chance that the writer can lap the reader in the interim between poll()
|
||||
* returning and the read() request.
|
||||
*/
|
||||
static unsigned int logger_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct logger_reader *reader;
|
||||
struct logger_log *log;
|
||||
unsigned int ret = POLLOUT | POLLWRNORM;
|
||||
|
||||
if (!(file->f_mode & FMODE_READ))
|
||||
return ret;
|
||||
|
||||
reader = file->private_data;
|
||||
log = reader->log;
|
||||
|
||||
poll_wait(file, &log->wq, wait);
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
if (log->w_off != reader->r_off)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long logger_set_version(struct logger_reader *reader, void __user *arg)
|
||||
{
|
||||
int version;
|
||||
|
||||
if (copy_from_user(&version, arg, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
if ((version < 1) || (version > 2))
|
||||
return -EINVAL;
|
||||
|
||||
reader->r_ver = version;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct logger_log *log = file_get_log(file);
|
||||
struct logger_reader *reader;
|
||||
long ret = -EINVAL;
|
||||
void __user *argp = (void __user *) arg;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
switch (cmd) {
|
||||
case LOGGER_GET_LOG_BUF_SIZE:
|
||||
ret = log->size;
|
||||
break;
|
||||
case LOGGER_GET_LOG_LEN:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
if (log->w_off >= reader->r_off)
|
||||
ret = log->w_off - reader->r_off;
|
||||
else
|
||||
ret = (log->size - reader->r_off) + log->w_off;
|
||||
break;
|
||||
case LOGGER_GET_NEXT_ENTRY_LEN:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
if (log->w_off != reader->r_off)
|
||||
ret = get_user_hdr_len(reader->r_ver) +
|
||||
get_entry_msg_len(log, reader->r_off);
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
case LOGGER_FLUSH_LOG:
|
||||
if (!(file->f_mode & FMODE_WRITE)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) ||
|
||||
capable(CAP_SYSLOG))) {
|
||||
ret = -EPERM;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(reader, &log->readers, list)
|
||||
reader->r_off = log->w_off;
|
||||
log->head = log->w_off;
|
||||
ret = 0;
|
||||
break;
|
||||
case LOGGER_GET_VERSION:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
ret = reader->r_ver;
|
||||
break;
|
||||
case LOGGER_SET_VERSION:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
ret = logger_set_version(reader, argp);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations logger_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = logger_read,
|
||||
.aio_write = logger_aio_write,
|
||||
.poll = logger_poll,
|
||||
.unlocked_ioctl = logger_ioctl,
|
||||
.compat_ioctl = logger_ioctl,
|
||||
.open = logger_open,
|
||||
.release = logger_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Log size must must be a power of two, and greater than
|
||||
* (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)).
|
||||
*/
|
||||
static int __init create_log(char *log_name, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct logger_log *log;
|
||||
unsigned char *buffer;
|
||||
|
||||
buffer = vmalloc(size);
|
||||
if (buffer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
log = kzalloc(sizeof(struct logger_log), GFP_KERNEL);
|
||||
if (log == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_buffer;
|
||||
}
|
||||
log->buffer = buffer;
|
||||
|
||||
log->misc.minor = MISC_DYNAMIC_MINOR;
|
||||
log->misc.name = kstrdup(log_name, GFP_KERNEL);
|
||||
if (log->misc.name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_log;
|
||||
}
|
||||
|
||||
log->misc.fops = &logger_fops;
|
||||
log->misc.parent = NULL;
|
||||
|
||||
init_waitqueue_head(&log->wq);
|
||||
INIT_LIST_HEAD(&log->readers);
|
||||
mutex_init(&log->mutex);
|
||||
log->w_off = 0;
|
||||
log->head = 0;
|
||||
log->size = size;
|
||||
|
||||
INIT_LIST_HEAD(&log->logs);
|
||||
list_add_tail(&log->logs, &log_list);
|
||||
|
||||
/* finally, initialize the misc device for this log */
|
||||
ret = misc_register(&log->misc);
|
||||
if (unlikely(ret)) {
|
||||
pr_err("failed to register misc device for log '%s'!\n",
|
||||
log->misc.name);
|
||||
goto out_free_log;
|
||||
}
|
||||
|
||||
pr_info("created %luK log '%s'\n",
|
||||
(unsigned long) log->size >> 10, log->misc.name);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_log:
|
||||
kfree(log);
|
||||
|
||||
out_free_buffer:
|
||||
vfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init logger_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = create_log(LOGGER_LOG_MAIN, 256*1024);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = create_log(LOGGER_LOG_RADIO, 256*1024);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit logger_exit(void)
|
||||
{
|
||||
struct logger_log *current_log, *next_log;
|
||||
|
||||
list_for_each_entry_safe(current_log, next_log, &log_list, logs) {
|
||||
/* we have to delete all the entry inside log_list */
|
||||
misc_deregister(¤t_log->misc);
|
||||
vfree(current_log->buffer);
|
||||
kfree(current_log->misc.name);
|
||||
list_del(¤t_log->logs);
|
||||
kfree(current_log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
device_initcall(logger_init);
|
||||
module_exit(logger_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Robert Love, <rlove@google.com>");
|
||||
MODULE_DESCRIPTION("Android Logger");
|
||||
@@ -1,89 +0,0 @@
|
||||
/* include/linux/logger.h
|
||||
*
|
||||
* Copyright (C) 2007-2008 Google, Inc.
|
||||
* Author: Robert Love <rlove@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_LOGGER_H
|
||||
#define _LINUX_LOGGER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/**
|
||||
* struct user_logger_entry_compat - defines a single entry that is given to a logger
|
||||
* @len: The length of the payload
|
||||
* @__pad: Two bytes of padding that appear to be required
|
||||
* @pid: The generating process' process ID
|
||||
* @tid: The generating process' thread ID
|
||||
* @sec: The number of seconds that have elapsed since the Epoch
|
||||
* @nsec: The number of nanoseconds that have elapsed since @sec
|
||||
* @msg: The message that is to be logged
|
||||
*
|
||||
* The userspace structure for version 1 of the logger_entry ABI.
|
||||
* This structure is returned to userspace unless the caller requests
|
||||
* an upgrade to a newer ABI version.
|
||||
*/
|
||||
struct user_logger_entry_compat {
|
||||
__u16 len;
|
||||
__u16 __pad;
|
||||
__s32 pid;
|
||||
__s32 tid;
|
||||
__s32 sec;
|
||||
__s32 nsec;
|
||||
char msg[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct logger_entry - defines a single entry that is given to a logger
|
||||
* @len: The length of the payload
|
||||
* @hdr_size: sizeof(struct logger_entry_v2)
|
||||
* @pid: The generating process' process ID
|
||||
* @tid: The generating process' thread ID
|
||||
* @sec: The number of seconds that have elapsed since the Epoch
|
||||
* @nsec: The number of nanoseconds that have elapsed since @sec
|
||||
* @euid: Effective UID of logger
|
||||
* @msg: The message that is to be logged
|
||||
*
|
||||
* The structure for version 2 of the logger_entry ABI.
|
||||
* This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
|
||||
* is called with version >= 2
|
||||
*/
|
||||
struct logger_entry {
|
||||
__u16 len;
|
||||
__u16 hdr_size;
|
||||
__s32 pid;
|
||||
__s32 tid;
|
||||
__s32 sec;
|
||||
__s32 nsec;
|
||||
kuid_t euid;
|
||||
char msg[0];
|
||||
};
|
||||
|
||||
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
|
||||
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
|
||||
#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
|
||||
#define LOGGER_LOG_MAIN "log_main" /* everything else */
|
||||
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4076
|
||||
|
||||
#define __LOGGERIO 0xAE
|
||||
|
||||
#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
|
||||
#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
|
||||
#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
|
||||
#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
|
||||
#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */
|
||||
#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
|
||||
|
||||
#endif /* _LINUX_LOGGER_H */
|
||||
@@ -1,62 +0,0 @@
|
||||
/* drivers/staging/android/uapi/android_alarm.h
|
||||
*
|
||||
* Copyright (C) 2006-2007 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_ANDROID_ALARM_H
|
||||
#define _UAPI_LINUX_ANDROID_ALARM_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
enum android_alarm_type {
|
||||
/* return code bit numbers or set alarm arg */
|
||||
ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME,
|
||||
|
||||
ANDROID_ALARM_TYPE_COUNT,
|
||||
|
||||
/* return code bit numbers */
|
||||
/* ANDROID_ALARM_TIME_CHANGE = 16 */
|
||||
};
|
||||
|
||||
enum android_alarm_return_flags {
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
|
||||
ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
|
||||
};
|
||||
|
||||
/* Disable alarm */
|
||||
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4))
|
||||
|
||||
/* Ack last alarm and wait for next */
|
||||
#define ANDROID_ALARM_WAIT _IO('a', 1)
|
||||
|
||||
#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
|
||||
/* Set alarm */
|
||||
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
|
||||
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
|
||||
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
|
||||
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "f_fs.c"
|
||||
#include "f_audio_source.c"
|
||||
#include "f_midi.c"
|
||||
#include "f_mass_storage.c"
|
||||
#include "f_mtp.c"
|
||||
#include "f_accessory.c"
|
||||
@@ -51,6 +52,12 @@ static const char longname[] = "Gadget Android";
|
||||
#define VENDOR_ID 0x18D1
|
||||
#define PRODUCT_ID 0x0001
|
||||
|
||||
/* f_midi configuration */
|
||||
#define MIDI_INPUT_PORTS 1
|
||||
#define MIDI_OUTPUT_PORTS 1
|
||||
#define MIDI_BUFFER_SIZE 256
|
||||
#define MIDI_QUEUE_LENGTH 32
|
||||
|
||||
struct android_usb_function {
|
||||
char *name;
|
||||
void *config;
|
||||
@@ -933,6 +940,60 @@ static struct android_usb_function audio_source_function = {
|
||||
.attributes = audio_source_function_attributes,
|
||||
};
|
||||
|
||||
static int midi_function_init(struct android_usb_function *f,
|
||||
struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct midi_alsa_config *config;
|
||||
|
||||
config = kzalloc(sizeof(struct midi_alsa_config), GFP_KERNEL);
|
||||
f->config = config;
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
config->card = -1;
|
||||
config->device = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_function_cleanup(struct android_usb_function *f)
|
||||
{
|
||||
kfree(f->config);
|
||||
}
|
||||
|
||||
static int midi_function_bind_config(struct android_usb_function *f,
|
||||
struct usb_configuration *c)
|
||||
{
|
||||
struct midi_alsa_config *config = f->config;
|
||||
|
||||
return f_midi_bind_config(c, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||
MIDI_INPUT_PORTS, MIDI_OUTPUT_PORTS, MIDI_BUFFER_SIZE,
|
||||
MIDI_QUEUE_LENGTH, config);
|
||||
}
|
||||
|
||||
static ssize_t midi_alsa_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct android_usb_function *f = dev_get_drvdata(dev);
|
||||
struct midi_alsa_config *config = f->config;
|
||||
|
||||
/* print ALSA card and device numbers */
|
||||
return sprintf(buf, "%d %d\n", config->card, config->device);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(alsa, S_IRUGO, midi_alsa_show, NULL);
|
||||
|
||||
static struct device_attribute *midi_function_attributes[] = {
|
||||
&dev_attr_alsa,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct android_usb_function midi_function = {
|
||||
.name = "midi",
|
||||
.init = midi_function_init,
|
||||
.cleanup = midi_function_cleanup,
|
||||
.bind_config = midi_function_bind_config,
|
||||
.attributes = midi_function_attributes,
|
||||
};
|
||||
|
||||
static struct android_usb_function *supported_functions[] = {
|
||||
&ffs_function,
|
||||
&acm_function,
|
||||
@@ -942,10 +1003,10 @@ static struct android_usb_function *supported_functions[] = {
|
||||
&mass_storage_function,
|
||||
&accessory_function,
|
||||
&audio_source_function,
|
||||
&midi_function,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static int android_init_functions(struct android_usb_function **functions,
|
||||
struct usb_composite_dev *cdev)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#define AUDIO_NUM_INTERFACES 2
|
||||
|
||||
/* B.3.1 Standard AC Interface Descriptor */
|
||||
static struct usb_interface_descriptor ac_interface_desc = {
|
||||
static struct usb_interface_descriptor audio_ac_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bNumEndpoints = 0,
|
||||
@@ -50,7 +50,7 @@ DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
|
||||
+ UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
|
||||
+ UAC_DT_FEATURE_UNIT_SIZE(0))
|
||||
/* B.3.2 Class-Specific AC Interface Descriptor */
|
||||
static struct uac1_ac_header_descriptor_2 ac_header_desc = {
|
||||
static struct uac1_ac_header_descriptor_2 audio_ac_header_desc = {
|
||||
.bLength = UAC_DT_AC_HEADER_LENGTH,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_HEADER,
|
||||
@@ -64,7 +64,7 @@ static struct uac1_ac_header_descriptor_2 ac_header_desc = {
|
||||
};
|
||||
|
||||
#define INPUT_TERMINAL_ID 1
|
||||
static struct uac_input_terminal_descriptor input_terminal_desc = {
|
||||
static struct uac_input_terminal_descriptor audio_input_terminal_desc = {
|
||||
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
||||
@@ -77,7 +77,7 @@ static struct uac_input_terminal_descriptor input_terminal_desc = {
|
||||
DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
|
||||
|
||||
#define FEATURE_UNIT_ID 2
|
||||
static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
|
||||
static struct uac_feature_unit_descriptor_0 audio_feature_unit_desc = {
|
||||
.bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_FEATURE_UNIT,
|
||||
@@ -87,7 +87,7 @@ static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
|
||||
};
|
||||
|
||||
#define OUTPUT_TERMINAL_ID 3
|
||||
static struct uac1_output_terminal_descriptor output_terminal_desc = {
|
||||
static struct uac1_output_terminal_descriptor audio_output_terminal_desc = {
|
||||
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
||||
@@ -98,7 +98,7 @@ static struct uac1_output_terminal_descriptor output_terminal_desc = {
|
||||
};
|
||||
|
||||
/* B.4.1 Standard AS Interface Descriptor */
|
||||
static struct usb_interface_descriptor as_interface_alt_0_desc = {
|
||||
static struct usb_interface_descriptor audio_as_interface_alt_0_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bAlternateSetting = 0,
|
||||
@@ -107,7 +107,7 @@ static struct usb_interface_descriptor as_interface_alt_0_desc = {
|
||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor as_interface_alt_1_desc = {
|
||||
static struct usb_interface_descriptor audio_as_interface_alt_1_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bAlternateSetting = 1,
|
||||
@@ -117,7 +117,7 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = {
|
||||
};
|
||||
|
||||
/* B.4.2 Class-Specific AS Interface Descriptor */
|
||||
static struct uac1_as_header_descriptor as_header_desc = {
|
||||
static struct uac1_as_header_descriptor audio_as_header_desc = {
|
||||
.bLength = UAC_DT_AS_HEADER_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_AS_GENERAL,
|
||||
@@ -128,7 +128,7 @@ static struct uac1_as_header_descriptor as_header_desc = {
|
||||
|
||||
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
|
||||
|
||||
static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
|
||||
static struct uac_format_type_i_discrete_descriptor_1 audio_as_type_i_desc = {
|
||||
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_FORMAT_TYPE,
|
||||
@@ -139,7 +139,7 @@ static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
|
||||
};
|
||||
|
||||
/* Standard ISO IN Endpoint Descriptor for highspeed */
|
||||
static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
|
||||
static struct usb_endpoint_descriptor audio_hs_as_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
@@ -150,7 +150,7 @@ static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
|
||||
};
|
||||
|
||||
/* Standard ISO IN Endpoint Descriptor for highspeed */
|
||||
static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
|
||||
static struct usb_endpoint_descriptor audio_fs_as_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
@@ -161,7 +161,7 @@ static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
|
||||
};
|
||||
|
||||
/* Class-specific AS ISO OUT Endpoint Descriptor */
|
||||
static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
|
||||
static struct uac_iso_endpoint_descriptor audio_as_iso_in_desc = {
|
||||
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||
.bDescriptorSubtype = UAC_EP_GENERAL,
|
||||
@@ -171,40 +171,40 @@ static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hs_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
(struct usb_descriptor_header *)&audio_ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&audio_ac_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&input_terminal_desc,
|
||||
(struct usb_descriptor_header *)&output_terminal_desc,
|
||||
(struct usb_descriptor_header *)&feature_unit_desc,
|
||||
(struct usb_descriptor_header *)&audio_input_terminal_desc,
|
||||
(struct usb_descriptor_header *)&audio_output_terminal_desc,
|
||||
(struct usb_descriptor_header *)&audio_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_header_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_type_i_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&hs_as_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_iso_in_desc,
|
||||
(struct usb_descriptor_header *)&audio_hs_as_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_iso_in_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fs_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
(struct usb_descriptor_header *)&audio_ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&audio_ac_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&input_terminal_desc,
|
||||
(struct usb_descriptor_header *)&output_terminal_desc,
|
||||
(struct usb_descriptor_header *)&feature_unit_desc,
|
||||
(struct usb_descriptor_header *)&audio_input_terminal_desc,
|
||||
(struct usb_descriptor_header *)&audio_output_terminal_desc,
|
||||
(struct usb_descriptor_header *)&audio_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_header_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_type_i_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&fs_as_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_iso_in_desc,
|
||||
(struct usb_descriptor_header *)&audio_fs_as_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&audio_as_iso_in_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -552,12 +552,12 @@ static void audio_build_desc(struct audio_dev *audio)
|
||||
int rate;
|
||||
|
||||
/* Set channel numbers */
|
||||
input_terminal_desc.bNrChannels = 2;
|
||||
as_type_i_desc.bNrChannels = 2;
|
||||
audio_input_terminal_desc.bNrChannels = 2;
|
||||
audio_as_type_i_desc.bNrChannels = 2;
|
||||
|
||||
/* Set sample rates */
|
||||
rate = SAMPLE_RATE;
|
||||
sam_freq = as_type_i_desc.tSamFreq[0];
|
||||
sam_freq = audio_as_type_i_desc.tSamFreq[0];
|
||||
memcpy(sam_freq, &rate, 3);
|
||||
}
|
||||
|
||||
@@ -578,32 +578,32 @@ audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
ac_interface_desc.bInterfaceNumber = status;
|
||||
audio_ac_interface_desc.bInterfaceNumber = status;
|
||||
|
||||
/* AUDIO_AC_INTERFACE */
|
||||
ac_header_desc.baInterfaceNr[0] = status;
|
||||
audio_ac_header_desc.baInterfaceNr[0] = status;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
as_interface_alt_0_desc.bInterfaceNumber = status;
|
||||
as_interface_alt_1_desc.bInterfaceNumber = status;
|
||||
audio_as_interface_alt_0_desc.bInterfaceNumber = status;
|
||||
audio_as_interface_alt_1_desc.bInterfaceNumber = status;
|
||||
|
||||
/* AUDIO_AS_INTERFACE */
|
||||
ac_header_desc.baInterfaceNr[1] = status;
|
||||
audio_ac_header_desc.baInterfaceNr[1] = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
/* allocate our endpoint */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &audio_fs_as_in_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
audio->in_ep = ep;
|
||||
ep->driver_data = audio; /* claim */
|
||||
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
hs_as_in_ep_desc.bEndpointAddress =
|
||||
fs_as_in_ep_desc.bEndpointAddress;
|
||||
audio_hs_as_in_ep_desc.bEndpointAddress =
|
||||
audio_fs_as_in_ep_desc.bEndpointAddress;
|
||||
|
||||
f->fs_descriptors = fs_audio_desc;
|
||||
f->hs_descriptors = hs_audio_desc;
|
||||
|
||||
@@ -65,6 +65,11 @@ struct gmidi_in_port {
|
||||
uint8_t data[2];
|
||||
};
|
||||
|
||||
struct midi_alsa_config {
|
||||
int card;
|
||||
int device;
|
||||
};
|
||||
|
||||
struct f_midi {
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
@@ -97,7 +102,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
|
||||
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
|
||||
|
||||
/* B.3.1 Standard AC Interface Descriptor */
|
||||
static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor ac_interface_desc /* __initdata */ = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
@@ -108,7 +113,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.3.2 Class-Specific AC Interface Descriptor */
|
||||
static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
|
||||
static struct uac1_ac_header_descriptor_1 ac_header_desc /* __initdata */ = {
|
||||
.bLength = UAC_DT_AC_HEADER_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_MS_HEADER,
|
||||
@@ -119,7 +124,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.4.1 Standard MS Interface Descriptor */
|
||||
static struct usb_interface_descriptor ms_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor ms_interface_desc /* __initdata */ = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
@@ -130,7 +135,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.4.2 Class-Specific MS Interface Descriptor */
|
||||
static struct usb_ms_header_descriptor ms_header_desc __initdata = {
|
||||
static struct usb_ms_header_descriptor ms_header_desc /* __initdata */ = {
|
||||
.bLength = USB_DT_MS_HEADER_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_MS_HEADER,
|
||||
@@ -191,7 +196,7 @@ static struct usb_gadget_strings *midi_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||
static struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
@@ -207,7 +212,7 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||
return req;
|
||||
}
|
||||
|
||||
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
static void midi_free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
@@ -278,7 +283,7 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
if (ep == midi->out_ep)
|
||||
f_midi_handle_out_data(ep, req);
|
||||
|
||||
free_ep_req(ep, req);
|
||||
midi_free_ep_req(ep, req);
|
||||
return;
|
||||
|
||||
case -EOVERFLOW: /* buffer overrun on read means that
|
||||
@@ -365,7 +370,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
/* allocate a bunch of read buffers and queue them all at once. */
|
||||
for (i = 0; i < midi->qlen && err == 0; i++) {
|
||||
struct usb_request *req =
|
||||
alloc_ep_req(midi->out_ep, midi->buflen);
|
||||
midi_alloc_ep_req(midi->out_ep, midi->buflen);
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -409,7 +414,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
card = midi->card;
|
||||
midi->card = NULL;
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
snd_card_free_when_closed(card);
|
||||
|
||||
kfree(midi->id);
|
||||
midi->id = NULL;
|
||||
@@ -546,10 +551,10 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
|
||||
return;
|
||||
|
||||
if (!req)
|
||||
req = alloc_ep_req(ep, midi->buflen);
|
||||
req = midi_alloc_ep_req(ep, midi->buflen);
|
||||
|
||||
if (!req) {
|
||||
ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
|
||||
ERROR(midi, "gmidi_transmit: midi_alloc_ep_request failed\n");
|
||||
return;
|
||||
}
|
||||
req->length = 0;
|
||||
@@ -575,7 +580,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
|
||||
if (req->length > 0)
|
||||
usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
else
|
||||
free_ep_req(ep, req);
|
||||
midi_free_ep_req(ep, req);
|
||||
}
|
||||
|
||||
static void f_midi_in_tasklet(unsigned long data)
|
||||
@@ -733,7 +738,7 @@ fail:
|
||||
|
||||
/* MIDI function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
static int /* __init */
|
||||
f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_descriptor_header **midi_function;
|
||||
@@ -923,16 +928,22 @@ fail:
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int __init f_midi_bind_config(struct usb_configuration *c,
|
||||
int /* __init */ f_midi_bind_config(struct usb_configuration *c,
|
||||
int index, char *id,
|
||||
unsigned int in_ports,
|
||||
unsigned int out_ports,
|
||||
unsigned int buflen,
|
||||
unsigned int qlen)
|
||||
unsigned int qlen,
|
||||
struct midi_alsa_config* config)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
int status, i;
|
||||
|
||||
if (config) {
|
||||
config->card = -1;
|
||||
config->device = -1;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
|
||||
return -EINVAL;
|
||||
@@ -961,6 +972,10 @@ int __init f_midi_bind_config(struct usb_configuration *c,
|
||||
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
|
||||
|
||||
/* set up ALSA midi devices */
|
||||
midi->id = kstrdup(id, GFP_KERNEL);
|
||||
midi->index = index;
|
||||
midi->buflen = buflen;
|
||||
midi->qlen = qlen;
|
||||
midi->in_ports = in_ports;
|
||||
midi->out_ports = out_ports;
|
||||
status = f_midi_register_card(midi);
|
||||
@@ -974,15 +989,16 @@ int __init f_midi_bind_config(struct usb_configuration *c,
|
||||
midi->func.set_alt = f_midi_set_alt;
|
||||
midi->func.disable = f_midi_disable;
|
||||
|
||||
midi->id = kstrdup(id, GFP_KERNEL);
|
||||
midi->index = index;
|
||||
midi->buflen = buflen;
|
||||
midi->qlen = qlen;
|
||||
|
||||
status = usb_add_function(c, &midi->func);
|
||||
if (status)
|
||||
goto setup_fail;
|
||||
|
||||
|
||||
if (config) {
|
||||
config->card = midi->rmidi->card->number;
|
||||
config->device = midi->rmidi->device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
setup_fail:
|
||||
|
||||
@@ -40,6 +40,11 @@ struct lsm_network_audit {
|
||||
} fam;
|
||||
};
|
||||
|
||||
struct lsm_ioctlop_audit {
|
||||
struct path path;
|
||||
u16 cmd;
|
||||
};
|
||||
|
||||
/* Auxiliary data to use in generating the audit record. */
|
||||
struct common_audit_data {
|
||||
char type;
|
||||
@@ -53,6 +58,7 @@ struct common_audit_data {
|
||||
#define LSM_AUDIT_DATA_KMOD 8
|
||||
#define LSM_AUDIT_DATA_INODE 9
|
||||
#define LSM_AUDIT_DATA_DENTRY 10
|
||||
#define LSM_AUDIT_DATA_IOCTL_OP 11
|
||||
union {
|
||||
struct path path;
|
||||
struct dentry *dentry;
|
||||
@@ -68,6 +74,7 @@ struct common_audit_data {
|
||||
} key_struct;
|
||||
#endif
|
||||
char *kmod_name;
|
||||
struct lsm_ioctlop_audit *op;
|
||||
} u;
|
||||
/* this union contains LSM specific data */
|
||||
union {
|
||||
|
||||
@@ -36,6 +36,11 @@ static char abort_reason[MAX_SUSPEND_ABORT_LEN];
|
||||
static struct kobject *wakeup_reason;
|
||||
static DEFINE_SPINLOCK(resume_reason_lock);
|
||||
|
||||
static struct timespec last_xtime; /* wall time before last suspend */
|
||||
static struct timespec curr_xtime; /* wall time after last suspend */
|
||||
static struct timespec last_stime; /* total_sleep_time before last suspend */
|
||||
static struct timespec curr_stime; /* total_sleep_time after last suspend */
|
||||
|
||||
static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@@ -59,10 +64,32 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu
|
||||
return buf_offset;
|
||||
}
|
||||
|
||||
static ssize_t last_suspend_time_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct timespec sleep_time;
|
||||
struct timespec total_time;
|
||||
struct timespec suspend_resume_time;
|
||||
|
||||
sleep_time = timespec_sub(curr_stime, last_stime);
|
||||
total_time = timespec_sub(curr_xtime, last_xtime);
|
||||
suspend_resume_time = timespec_sub(total_time, sleep_time);
|
||||
|
||||
/*
|
||||
* suspend_resume_time is calculated from sleep_time. Userspace would
|
||||
* always need both. Export them in pair here.
|
||||
*/
|
||||
return sprintf(buf, "%lu.%09lu %lu.%09lu\n",
|
||||
suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec,
|
||||
sleep_time.tv_sec, sleep_time.tv_nsec);
|
||||
}
|
||||
|
||||
static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason);
|
||||
static struct kobj_attribute suspend_time = __ATTR_RO(last_suspend_time);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&resume_reason.attr,
|
||||
&suspend_time.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group attr_group = {
|
||||
@@ -133,12 +160,21 @@ void log_suspend_abort_reason(const char *fmt, ...)
|
||||
static int wakeup_reason_pm_event(struct notifier_block *notifier,
|
||||
unsigned long pm_event, void *unused)
|
||||
{
|
||||
struct timespec xtom; /* wall_to_monotonic, ignored */
|
||||
|
||||
switch (pm_event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
spin_lock(&resume_reason_lock);
|
||||
irqcount = 0;
|
||||
suspend_abort = false;
|
||||
spin_unlock(&resume_reason_lock);
|
||||
|
||||
get_xtime_and_monotonic_and_sleep_offset(&last_xtime, &xtom,
|
||||
&last_stime);
|
||||
break;
|
||||
case PM_POST_SUSPEND:
|
||||
get_xtime_and_monotonic_and_sleep_offset(&curr_xtime, &xtom,
|
||||
&curr_stime);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -245,6 +245,21 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_IOCTL_OP: {
|
||||
struct inode *inode;
|
||||
|
||||
audit_log_d_path(ab, " path=", &a->u.op->path);
|
||||
|
||||
inode = a->u.op->path.dentry->d_inode;
|
||||
if (inode) {
|
||||
audit_log_format(ab, " dev=");
|
||||
audit_log_untrustedstring(ab, inode->i_sb->s_id);
|
||||
audit_log_format(ab, " ino=%lu", inode->i_ino);
|
||||
}
|
||||
|
||||
audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_DENTRY: {
|
||||
struct inode *inode;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/list.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/un.h>
|
||||
#include <net/af_unix.h>
|
||||
@@ -48,6 +49,7 @@ struct avc_entry {
|
||||
u32 tsid;
|
||||
u16 tclass;
|
||||
struct av_decision avd;
|
||||
struct avc_operation_node *ops_node;
|
||||
};
|
||||
|
||||
struct avc_node {
|
||||
@@ -64,6 +66,16 @@ struct avc_cache {
|
||||
u32 latest_notif; /* latest revocation notification */
|
||||
};
|
||||
|
||||
struct avc_operation_decision_node {
|
||||
struct operation_decision od;
|
||||
struct list_head od_list;
|
||||
};
|
||||
|
||||
struct avc_operation_node {
|
||||
struct operation ops;
|
||||
struct list_head od_head; /* list of operation_decision_node */
|
||||
};
|
||||
|
||||
struct avc_callback_node {
|
||||
int (*callback) (u32 event);
|
||||
u32 events;
|
||||
@@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
|
||||
static struct avc_cache avc_cache;
|
||||
static struct avc_callback_node *avc_callbacks;
|
||||
static struct kmem_cache *avc_node_cachep;
|
||||
static struct kmem_cache *avc_operation_decision_node_cachep;
|
||||
static struct kmem_cache *avc_operation_node_cachep;
|
||||
static struct kmem_cache *avc_operation_perm_cachep;
|
||||
|
||||
static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
|
||||
{
|
||||
@@ -171,6 +186,16 @@ void __init avc_init(void)
|
||||
|
||||
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avc_operation_node_cachep = kmem_cache_create("avc_operation_node",
|
||||
sizeof(struct avc_operation_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avc_operation_decision_node_cachep = kmem_cache_create(
|
||||
"avc_operation_decision_node",
|
||||
sizeof(struct avc_operation_decision_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avc_operation_perm_cachep = kmem_cache_create("avc_operation_perm",
|
||||
sizeof(struct operation_perm),
|
||||
0, SLAB_PANIC, NULL);
|
||||
|
||||
audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
|
||||
}
|
||||
@@ -205,9 +230,269 @@ int avc_get_hash_stats(char *page)
|
||||
slots_used, AVC_CACHE_SLOTS, max_chain_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* using a linked list for operation_decision lookup because the list is
|
||||
* always small. i.e. less than 5, typically 1
|
||||
*/
|
||||
static struct operation_decision *avc_operation_lookup(u8 type,
|
||||
struct avc_operation_node *ops_node)
|
||||
{
|
||||
struct avc_operation_decision_node *od_node;
|
||||
struct operation_decision *od = NULL;
|
||||
|
||||
list_for_each_entry(od_node, &ops_node->od_head, od_list) {
|
||||
if (od_node->od.type != type)
|
||||
continue;
|
||||
od = &od_node->od;
|
||||
break;
|
||||
}
|
||||
return od;
|
||||
}
|
||||
|
||||
static inline unsigned int avc_operation_has_perm(struct operation_decision *od,
|
||||
u16 cmd, u8 specified)
|
||||
{
|
||||
unsigned int rc = 0;
|
||||
u8 num = cmd & 0xff;
|
||||
|
||||
if ((specified == OPERATION_ALLOWED) &&
|
||||
(od->specified & OPERATION_ALLOWED))
|
||||
rc = security_operation_test(od->allowed->perms, num);
|
||||
else if ((specified == OPERATION_AUDITALLOW) &&
|
||||
(od->specified & OPERATION_AUDITALLOW))
|
||||
rc = security_operation_test(od->auditallow->perms, num);
|
||||
else if ((specified == OPERATION_DONTAUDIT) &&
|
||||
(od->specified & OPERATION_DONTAUDIT))
|
||||
rc = security_operation_test(od->dontaudit->perms, num);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void avc_operation_allow_perm(struct avc_operation_node *node, u16 cmd)
|
||||
{
|
||||
struct operation_decision *od;
|
||||
u8 type;
|
||||
u8 num;
|
||||
|
||||
type = cmd >> 8;
|
||||
num = cmd & 0xff;
|
||||
security_operation_set(node->ops.type, type);
|
||||
od = avc_operation_lookup(type, node);
|
||||
if (od && od->allowed)
|
||||
security_operation_set(od->allowed->perms, num);
|
||||
}
|
||||
|
||||
static void avc_operation_decision_free(
|
||||
struct avc_operation_decision_node *od_node)
|
||||
{
|
||||
struct operation_decision *od;
|
||||
|
||||
od = &od_node->od;
|
||||
if (od->allowed)
|
||||
kmem_cache_free(avc_operation_perm_cachep, od->allowed);
|
||||
if (od->auditallow)
|
||||
kmem_cache_free(avc_operation_perm_cachep, od->auditallow);
|
||||
if (od->dontaudit)
|
||||
kmem_cache_free(avc_operation_perm_cachep, od->dontaudit);
|
||||
kmem_cache_free(avc_operation_decision_node_cachep, od_node);
|
||||
}
|
||||
|
||||
static void avc_operation_free(struct avc_operation_node *ops_node)
|
||||
{
|
||||
struct avc_operation_decision_node *od_node;
|
||||
|
||||
if (!ops_node)
|
||||
return;
|
||||
|
||||
list_for_each_entry(od_node, &ops_node->od_head, od_list)
|
||||
avc_operation_decision_free(od_node);
|
||||
kmem_cache_free(avc_operation_node_cachep, ops_node);
|
||||
}
|
||||
|
||||
static void avc_copy_operation_decision(struct operation_decision *dest,
|
||||
struct operation_decision *src)
|
||||
{
|
||||
dest->type = src->type;
|
||||
dest->specified = src->specified;
|
||||
if (dest->specified & OPERATION_ALLOWED)
|
||||
memcpy(dest->allowed->perms, src->allowed->perms,
|
||||
sizeof(src->allowed->perms));
|
||||
if (dest->specified & OPERATION_AUDITALLOW)
|
||||
memcpy(dest->auditallow->perms, src->auditallow->perms,
|
||||
sizeof(src->auditallow->perms));
|
||||
if (dest->specified & OPERATION_DONTAUDIT)
|
||||
memcpy(dest->dontaudit->perms, src->dontaudit->perms,
|
||||
sizeof(src->dontaudit->perms));
|
||||
}
|
||||
|
||||
/*
|
||||
* similar to avc_copy_operation_decision, but only copy decision
|
||||
* information relevant to this command
|
||||
*/
|
||||
static inline void avc_quick_copy_operation_decision(u16 cmd,
|
||||
struct operation_decision *dest,
|
||||
struct operation_decision *src)
|
||||
{
|
||||
/*
|
||||
* compute index of the u32 of the 256 bits (8 u32s) that contain this
|
||||
* command permission
|
||||
*/
|
||||
u8 i = (0xff & cmd) >> 5;
|
||||
|
||||
dest->specified = src->specified;
|
||||
if (dest->specified & OPERATION_ALLOWED)
|
||||
dest->allowed->perms[i] = src->allowed->perms[i];
|
||||
if (dest->specified & OPERATION_AUDITALLOW)
|
||||
dest->auditallow->perms[i] = src->auditallow->perms[i];
|
||||
if (dest->specified & OPERATION_DONTAUDIT)
|
||||
dest->dontaudit->perms[i] = src->dontaudit->perms[i];
|
||||
}
|
||||
|
||||
static struct avc_operation_decision_node
|
||||
*avc_operation_decision_alloc(u8 specified)
|
||||
{
|
||||
struct avc_operation_decision_node *node;
|
||||
struct operation_decision *od;
|
||||
|
||||
node = kmem_cache_zalloc(avc_operation_decision_node_cachep,
|
||||
GFP_ATOMIC | __GFP_NOMEMALLOC);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
od = &node->od;
|
||||
if (specified & OPERATION_ALLOWED) {
|
||||
od->allowed = kmem_cache_zalloc(avc_operation_perm_cachep,
|
||||
GFP_ATOMIC | __GFP_NOMEMALLOC);
|
||||
if (!od->allowed)
|
||||
goto error;
|
||||
}
|
||||
if (specified & OPERATION_AUDITALLOW) {
|
||||
od->auditallow = kmem_cache_zalloc(avc_operation_perm_cachep,
|
||||
GFP_ATOMIC | __GFP_NOMEMALLOC);
|
||||
if (!od->auditallow)
|
||||
goto error;
|
||||
}
|
||||
if (specified & OPERATION_DONTAUDIT) {
|
||||
od->dontaudit = kmem_cache_zalloc(avc_operation_perm_cachep,
|
||||
GFP_ATOMIC | __GFP_NOMEMALLOC);
|
||||
if (!od->dontaudit)
|
||||
goto error;
|
||||
}
|
||||
return node;
|
||||
error:
|
||||
avc_operation_decision_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int avc_add_operation(struct avc_node *node,
|
||||
struct operation_decision *od)
|
||||
{
|
||||
struct avc_operation_decision_node *dest_od;
|
||||
|
||||
node->ae.ops_node->ops.len++;
|
||||
dest_od = avc_operation_decision_alloc(od->specified);
|
||||
if (!dest_od)
|
||||
return -ENOMEM;
|
||||
avc_copy_operation_decision(&dest_od->od, od);
|
||||
list_add(&dest_od->od_list, &node->ae.ops_node->od_head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct avc_operation_node *avc_operation_alloc(void)
|
||||
{
|
||||
struct avc_operation_node *ops;
|
||||
|
||||
ops = kmem_cache_zalloc(avc_operation_node_cachep,
|
||||
GFP_ATOMIC|__GFP_NOMEMALLOC);
|
||||
if (!ops)
|
||||
return ops;
|
||||
INIT_LIST_HEAD(&ops->od_head);
|
||||
return ops;
|
||||
}
|
||||
|
||||
static int avc_operation_populate(struct avc_node *node,
|
||||
struct avc_operation_node *src)
|
||||
{
|
||||
struct avc_operation_node *dest;
|
||||
struct avc_operation_decision_node *dest_od;
|
||||
struct avc_operation_decision_node *src_od;
|
||||
|
||||
if (src->ops.len == 0)
|
||||
return 0;
|
||||
dest = avc_operation_alloc();
|
||||
if (!dest)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(dest->ops.type, &src->ops.type, sizeof(dest->ops.type));
|
||||
dest->ops.len = src->ops.len;
|
||||
|
||||
/* for each source od allocate a destination od and copy */
|
||||
list_for_each_entry(src_od, &src->od_head, od_list) {
|
||||
dest_od = avc_operation_decision_alloc(src_od->od.specified);
|
||||
if (!dest_od)
|
||||
goto error;
|
||||
avc_copy_operation_decision(&dest_od->od, &src_od->od);
|
||||
list_add(&dest_od->od_list, &dest->od_head);
|
||||
}
|
||||
node->ae.ops_node = dest;
|
||||
return 0;
|
||||
error:
|
||||
avc_operation_free(dest);
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
|
||||
static inline u32 avc_operation_audit_required(u32 requested,
|
||||
struct av_decision *avd,
|
||||
struct operation_decision *od,
|
||||
u16 cmd,
|
||||
int result,
|
||||
u32 *deniedp)
|
||||
{
|
||||
u32 denied, audited;
|
||||
|
||||
denied = requested & ~avd->allowed;
|
||||
if (unlikely(denied)) {
|
||||
audited = denied & avd->auditdeny;
|
||||
if (audited && od) {
|
||||
if (avc_operation_has_perm(od, cmd,
|
||||
OPERATION_DONTAUDIT))
|
||||
audited &= ~requested;
|
||||
}
|
||||
} else if (result) {
|
||||
audited = denied = requested;
|
||||
} else {
|
||||
audited = requested & avd->auditallow;
|
||||
if (audited && od) {
|
||||
if (!avc_operation_has_perm(od, cmd,
|
||||
OPERATION_AUDITALLOW))
|
||||
audited &= ~requested;
|
||||
}
|
||||
}
|
||||
|
||||
*deniedp = denied;
|
||||
return audited;
|
||||
}
|
||||
|
||||
static inline int avc_operation_audit(u32 ssid, u32 tsid, u16 tclass,
|
||||
u32 requested, struct av_decision *avd,
|
||||
struct operation_decision *od,
|
||||
u16 cmd, int result,
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
u32 audited, denied;
|
||||
|
||||
audited = avc_operation_audit_required(
|
||||
requested, avd, od, cmd, result, &denied);
|
||||
if (likely(!audited))
|
||||
return 0;
|
||||
return slow_avc_audit(ssid, tsid, tclass, requested,
|
||||
audited, denied, result, ad, 0);
|
||||
}
|
||||
|
||||
static void avc_node_free(struct rcu_head *rhead)
|
||||
{
|
||||
struct avc_node *node = container_of(rhead, struct avc_node, rhead);
|
||||
avc_operation_free(node->ae.ops_node);
|
||||
kmem_cache_free(avc_node_cachep, node);
|
||||
avc_cache_stats_incr(frees);
|
||||
}
|
||||
@@ -221,6 +506,7 @@ static void avc_node_delete(struct avc_node *node)
|
||||
|
||||
static void avc_node_kill(struct avc_node *node)
|
||||
{
|
||||
avc_operation_free(node->ae.ops_node);
|
||||
kmem_cache_free(avc_node_cachep, node);
|
||||
avc_cache_stats_incr(frees);
|
||||
atomic_dec(&avc_cache.active_nodes);
|
||||
@@ -367,6 +653,7 @@ static int avc_latest_notif_update(int seqno, int is_insert)
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @avd: resulting av decision
|
||||
* @ops: resulting operation decisions
|
||||
*
|
||||
* Insert an AVC entry for the SID pair
|
||||
* (@ssid, @tsid) and class @tclass.
|
||||
@@ -378,7 +665,9 @@ static int avc_latest_notif_update(int seqno, int is_insert)
|
||||
* the access vectors into a cache entry, returns
|
||||
* avc_node inserted. Otherwise, this function returns NULL.
|
||||
*/
|
||||
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
|
||||
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
|
||||
struct av_decision *avd,
|
||||
struct avc_operation_node *ops_node)
|
||||
{
|
||||
struct avc_node *pos, *node = NULL;
|
||||
int hvalue;
|
||||
@@ -391,10 +680,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec
|
||||
if (node) {
|
||||
struct hlist_head *head;
|
||||
spinlock_t *lock;
|
||||
int rc = 0;
|
||||
|
||||
hvalue = avc_hash(ssid, tsid, tclass);
|
||||
avc_node_populate(node, ssid, tsid, tclass, avd);
|
||||
|
||||
rc = avc_operation_populate(node, ops_node);
|
||||
if (rc) {
|
||||
kmem_cache_free(avc_node_cachep, node);
|
||||
return NULL;
|
||||
}
|
||||
head = &avc_cache.slots[hvalue];
|
||||
lock = &avc_cache.slots_lock[hvalue];
|
||||
|
||||
@@ -528,14 +822,17 @@ static inline int avc_sidcmp(u32 x, u32 y)
|
||||
* @perms : Permission mask bits
|
||||
* @ssid,@tsid,@tclass : identifier of an AVC entry
|
||||
* @seqno : sequence number when decision was made
|
||||
* @od: operation_decision to be added to the node
|
||||
*
|
||||
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
|
||||
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
|
||||
* otherwise, this function updates the AVC entry. The original AVC-entry object
|
||||
* will release later by RCU.
|
||||
*/
|
||||
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
|
||||
u32 seqno)
|
||||
static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 seqno,
|
||||
struct operation_decision *od,
|
||||
u32 flags)
|
||||
{
|
||||
int hvalue, rc = 0;
|
||||
unsigned long flag;
|
||||
@@ -579,9 +876,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
|
||||
|
||||
avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
|
||||
|
||||
if (orig->ae.ops_node) {
|
||||
rc = avc_operation_populate(node, orig->ae.ops_node);
|
||||
if (rc) {
|
||||
kmem_cache_free(avc_node_cachep, node);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case AVC_CALLBACK_GRANT:
|
||||
node->ae.avd.allowed |= perms;
|
||||
if (node->ae.ops_node && (flags & AVC_OPERATION_CMD))
|
||||
avc_operation_allow_perm(node->ae.ops_node, cmd);
|
||||
break;
|
||||
case AVC_CALLBACK_TRY_REVOKE:
|
||||
case AVC_CALLBACK_REVOKE:
|
||||
@@ -599,6 +906,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
|
||||
case AVC_CALLBACK_AUDITDENY_DISABLE:
|
||||
node->ae.avd.auditdeny &= ~perms;
|
||||
break;
|
||||
case AVC_CALLBACK_ADD_OPERATION:
|
||||
avc_add_operation(node, od);
|
||||
break;
|
||||
}
|
||||
avc_node_replace(node, orig);
|
||||
out_unlock:
|
||||
@@ -670,18 +980,20 @@ int avc_ss_reset(u32 seqno)
|
||||
* results in a bigger stack frame.
|
||||
*/
|
||||
static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
|
||||
u16 tclass, struct av_decision *avd)
|
||||
u16 tclass, struct av_decision *avd,
|
||||
struct avc_operation_node *ops_node)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
security_compute_av(ssid, tsid, tclass, avd);
|
||||
INIT_LIST_HEAD(&ops_node->od_head);
|
||||
security_compute_av(ssid, tsid, tclass, avd, &ops_node->ops);
|
||||
rcu_read_lock();
|
||||
return avc_insert(ssid, tsid, tclass, avd);
|
||||
return avc_insert(ssid, tsid, tclass, avd, ops_node);
|
||||
}
|
||||
|
||||
static noinline int avc_denied(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
unsigned flags,
|
||||
struct av_decision *avd)
|
||||
u16 tclass, u32 requested,
|
||||
u16 cmd, unsigned flags,
|
||||
struct av_decision *avd)
|
||||
{
|
||||
if (flags & AVC_STRICT)
|
||||
return -EACCES;
|
||||
@@ -689,11 +1001,92 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
|
||||
if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
|
||||
return -EACCES;
|
||||
|
||||
avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
|
||||
tsid, tclass, avd->seqno);
|
||||
avc_update_node(AVC_CALLBACK_GRANT, requested, cmd, ssid,
|
||||
tsid, tclass, avd->seqno, NULL, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl commands are comprised of four fields, direction, size, type, and
|
||||
* number. The avc operation logic filters based on two of them:
|
||||
*
|
||||
* type: or code, typically unique to each driver
|
||||
* number: or function
|
||||
*
|
||||
* For example, 0x89 is a socket type, and number 0x27 is the get hardware
|
||||
* address function.
|
||||
*/
|
||||
int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested,
|
||||
u16 cmd, struct common_audit_data *ad)
|
||||
{
|
||||
struct avc_node *node;
|
||||
struct av_decision avd;
|
||||
u32 denied;
|
||||
struct operation_decision *od = NULL;
|
||||
struct operation_decision od_local;
|
||||
struct operation_perm allowed;
|
||||
struct operation_perm auditallow;
|
||||
struct operation_perm dontaudit;
|
||||
struct avc_operation_node local_ops_node;
|
||||
struct avc_operation_node *ops_node;
|
||||
u8 type = cmd >> 8;
|
||||
int rc = 0, rc2;
|
||||
|
||||
ops_node = &local_ops_node;
|
||||
BUG_ON(!requested);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
node = avc_lookup(ssid, tsid, tclass);
|
||||
if (unlikely(!node)) {
|
||||
node = avc_compute_av(ssid, tsid, tclass, &avd, ops_node);
|
||||
} else {
|
||||
memcpy(&avd, &node->ae.avd, sizeof(avd));
|
||||
ops_node = node->ae.ops_node;
|
||||
}
|
||||
/* if operations are not defined, only consider av_decision */
|
||||
if (!ops_node || !ops_node->ops.len)
|
||||
goto decision;
|
||||
|
||||
od_local.allowed = &allowed;
|
||||
od_local.auditallow = &auditallow;
|
||||
od_local.dontaudit = &dontaudit;
|
||||
|
||||
/* lookup operation decision */
|
||||
od = avc_operation_lookup(type, ops_node);
|
||||
if (unlikely(!od)) {
|
||||
/* Compute operation decision if type is flagged */
|
||||
if (!security_operation_test(ops_node->ops.type, type)) {
|
||||
avd.allowed &= ~requested;
|
||||
goto decision;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
security_compute_operation(ssid, tsid, tclass, type, &od_local);
|
||||
rcu_read_lock();
|
||||
avc_update_node(AVC_CALLBACK_ADD_OPERATION, requested, cmd,
|
||||
ssid, tsid, tclass, avd.seqno, &od_local, 0);
|
||||
} else {
|
||||
avc_quick_copy_operation_decision(cmd, &od_local, od);
|
||||
}
|
||||
od = &od_local;
|
||||
|
||||
if (!avc_operation_has_perm(od, cmd, OPERATION_ALLOWED))
|
||||
avd.allowed &= ~requested;
|
||||
|
||||
decision:
|
||||
denied = requested & ~(avd.allowed);
|
||||
if (unlikely(denied))
|
||||
rc = avc_denied(ssid, tsid, tclass, requested, cmd,
|
||||
AVC_OPERATION_CMD, &avd);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
rc2 = avc_operation_audit(ssid, tsid, tclass, requested,
|
||||
&avd, od, cmd, rc, ad);
|
||||
if (rc2)
|
||||
return rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* avc_has_perm_noaudit - Check permissions but perform no auditing.
|
||||
@@ -721,6 +1114,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
struct av_decision *avd)
|
||||
{
|
||||
struct avc_node *node;
|
||||
struct avc_operation_node ops_node;
|
||||
int rc = 0;
|
||||
u32 denied;
|
||||
|
||||
@@ -729,16 +1123,14 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
rcu_read_lock();
|
||||
|
||||
node = avc_lookup(ssid, tsid, tclass);
|
||||
if (unlikely(!node)) {
|
||||
node = avc_compute_av(ssid, tsid, tclass, avd);
|
||||
} else {
|
||||
if (unlikely(!node))
|
||||
node = avc_compute_av(ssid, tsid, tclass, avd, &ops_node);
|
||||
else
|
||||
memcpy(avd, &node->ae.avd, sizeof(*avd));
|
||||
avd = &node->ae.avd;
|
||||
}
|
||||
|
||||
denied = requested & ~(avd->allowed);
|
||||
if (unlikely(denied))
|
||||
rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);
|
||||
rc = avc_denied(ssid, tsid, tclass, requested, 0, flags, avd);
|
||||
|
||||
rcu_read_unlock();
|
||||
return rc;
|
||||
|
||||
@@ -3108,6 +3108,44 @@ static void selinux_file_free_security(struct file *file)
|
||||
file_free_security(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
*/
|
||||
int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
u32 requested, u16 cmd)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct lsm_ioctlop_audit ioctl;
|
||||
u32 ssid = cred_sid(cred);
|
||||
int rc;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IOCTL_OP;
|
||||
ad.u.op = &ioctl;
|
||||
ad.u.op->cmd = cmd;
|
||||
ad.u.op->path = file->f_path;
|
||||
|
||||
if (ssid != fsec->sid) {
|
||||
rc = avc_has_perm(ssid, fsec->sid,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
rc = avc_has_operation(ssid, isec->sid, isec->sclass,
|
||||
requested, cmd, &ad);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@@ -3150,7 +3188,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
* to the file's ioctl() function.
|
||||
*/
|
||||
default:
|
||||
error = file_has_perm(cred, file, FILE__IOCTL);
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -142,11 +142,15 @@ static inline int avc_audit(u32 ssid, u32 tsid,
|
||||
}
|
||||
|
||||
#define AVC_STRICT 1 /* Ignore permissive mode. */
|
||||
#define AVC_OPERATION_CMD 2 /* ignore command when updating operations */
|
||||
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
unsigned flags,
|
||||
struct av_decision *avd);
|
||||
|
||||
int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested,
|
||||
u16 cmd, struct common_audit_data *ad);
|
||||
|
||||
int avc_has_perm_flags(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct common_audit_data *auditdata,
|
||||
@@ -169,6 +173,7 @@ u32 avc_policy_seqno(void);
|
||||
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
|
||||
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
|
||||
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
|
||||
#define AVC_CALLBACK_ADD_OPERATION 256
|
||||
|
||||
int avc_add_callback(int (*callback)(u32 event), u32 events);
|
||||
|
||||
|
||||
@@ -34,13 +34,14 @@
|
||||
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
|
||||
#define POLICYDB_VERSION_DEFAULT_TYPE 28
|
||||
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
|
||||
#define POLICYDB_VERSION_IOCTL_OPERATIONS 30
|
||||
|
||||
/* Range of policy versions we understand*/
|
||||
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
|
||||
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
|
||||
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
|
||||
#else
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IOCTL_OPERATIONS
|
||||
#endif
|
||||
|
||||
/* Mask for just the mount related flags */
|
||||
@@ -103,11 +104,40 @@ struct av_decision {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#define security_operation_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f))
|
||||
#define security_operation_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f)))
|
||||
|
||||
struct operation_perm {
|
||||
u32 perms[8];
|
||||
};
|
||||
|
||||
struct operation_decision {
|
||||
u8 type;
|
||||
u8 specified;
|
||||
struct operation_perm *allowed;
|
||||
struct operation_perm *auditallow;
|
||||
struct operation_perm *dontaudit;
|
||||
};
|
||||
|
||||
#define OPERATION_ALLOWED 1
|
||||
#define OPERATION_AUDITALLOW 2
|
||||
#define OPERATION_DONTAUDIT 4
|
||||
#define OPERATION_ALL (OPERATION_ALLOWED | OPERATION_AUDITALLOW |\
|
||||
OPERATION_DONTAUDIT)
|
||||
struct operation {
|
||||
u16 len; /* length of operation decision chain */
|
||||
u32 type[8]; /* 256 types */
|
||||
};
|
||||
|
||||
/* definitions of av_decision.flags */
|
||||
#define AVD_FLAGS_PERMISSIVE 0x0001
|
||||
|
||||
void security_compute_av(u32 ssid, u32 tsid,
|
||||
u16 tclass, struct av_decision *avd);
|
||||
u16 tclass, struct av_decision *avd,
|
||||
struct operation *ops);
|
||||
|
||||
void security_compute_operation(u32 ssid, u32 tsid, u16 tclass,
|
||||
u8 type, struct operation_decision *od);
|
||||
|
||||
void security_compute_av_user(u32 ssid, u32 tsid,
|
||||
u16 tclass, struct av_decision *avd);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/inet_diag.h>
|
||||
#include <linux/xfrm.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/sock_diag.h>
|
||||
|
||||
#include "flask.h"
|
||||
#include "av_permissions.h"
|
||||
@@ -78,6 +79,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
||||
{
|
||||
{ TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
|
||||
{ DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
|
||||
{ SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_xfrm_perms[] =
|
||||
@@ -98,6 +100,13 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] =
|
||||
{ XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_REPORT, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_MIGRATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_audit_perms[] =
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "policydb.h"
|
||||
|
||||
static struct kmem_cache *avtab_node_cachep;
|
||||
static struct kmem_cache *avtab_operation_cachep;
|
||||
|
||||
static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
|
||||
{
|
||||
@@ -37,11 +38,24 @@ avtab_insert_node(struct avtab *h, int hvalue,
|
||||
struct avtab_key *key, struct avtab_datum *datum)
|
||||
{
|
||||
struct avtab_node *newnode;
|
||||
struct avtab_operation *ops;
|
||||
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
|
||||
if (newnode == NULL)
|
||||
return NULL;
|
||||
newnode->key = *key;
|
||||
newnode->datum = *datum;
|
||||
|
||||
if (key->specified & AVTAB_OP) {
|
||||
ops = kmem_cache_zalloc(avtab_operation_cachep, GFP_KERNEL);
|
||||
if (ops == NULL) {
|
||||
kmem_cache_free(avtab_node_cachep, newnode);
|
||||
return NULL;
|
||||
}
|
||||
*ops = *(datum->u.ops);
|
||||
newnode->datum.u.ops = ops;
|
||||
} else {
|
||||
newnode->datum.u.data = datum->u.data;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
newnode->next = prev->next;
|
||||
prev->next = newnode;
|
||||
@@ -70,8 +84,11 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
key->target_type == cur->key.target_type &&
|
||||
key->target_class == cur->key.target_class &&
|
||||
(specified & cur->key.specified))
|
||||
(specified & cur->key.specified)) {
|
||||
if (specified & AVTAB_OPNUM)
|
||||
break;
|
||||
return -EEXIST;
|
||||
}
|
||||
if (key->source_type < cur->key.source_type)
|
||||
break;
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
@@ -232,6 +249,9 @@ void avtab_destroy(struct avtab *h)
|
||||
while (cur) {
|
||||
temp = cur;
|
||||
cur = cur->next;
|
||||
if (temp->key.specified & AVTAB_OP)
|
||||
kmem_cache_free(avtab_operation_cachep,
|
||||
temp->datum.u.ops);
|
||||
kmem_cache_free(avtab_node_cachep, temp);
|
||||
}
|
||||
h->htable[i] = NULL;
|
||||
@@ -320,7 +340,13 @@ static uint16_t spec_order[] = {
|
||||
AVTAB_AUDITALLOW,
|
||||
AVTAB_TRANSITION,
|
||||
AVTAB_CHANGE,
|
||||
AVTAB_MEMBER
|
||||
AVTAB_MEMBER,
|
||||
AVTAB_OPNUM_ALLOWED,
|
||||
AVTAB_OPNUM_AUDITALLOW,
|
||||
AVTAB_OPNUM_DONTAUDIT,
|
||||
AVTAB_OPTYPE_ALLOWED,
|
||||
AVTAB_OPTYPE_AUDITALLOW,
|
||||
AVTAB_OPTYPE_DONTAUDIT
|
||||
};
|
||||
|
||||
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
@@ -330,10 +356,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
{
|
||||
__le16 buf16[4];
|
||||
u16 enabled;
|
||||
__le32 buf32[7];
|
||||
u32 items, items2, val, vers = pol->policyvers;
|
||||
struct avtab_key key;
|
||||
struct avtab_datum datum;
|
||||
struct avtab_operation ops;
|
||||
__le32 buf32[ARRAY_SIZE(ops.op.perms)];
|
||||
int i, rc;
|
||||
unsigned set;
|
||||
|
||||
@@ -390,11 +417,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (val & AVTAB_OP) {
|
||||
printk(KERN_ERR "SELinux: avtab: entry has operations\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
|
||||
if (val & spec_order[i]) {
|
||||
key.specified = spec_order[i] | enabled;
|
||||
datum.data = le32_to_cpu(buf32[items++]);
|
||||
datum.u.data = le32_to_cpu(buf32[items++]);
|
||||
rc = insertf(a, &key, &datum, p);
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -413,7 +444,6 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
items = 0;
|
||||
key.source_type = le16_to_cpu(buf16[items++]);
|
||||
key.target_type = le16_to_cpu(buf16[items++]);
|
||||
@@ -437,14 +467,32 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = next_entry(buf32, fp, sizeof(u32));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS)
|
||||
|| !(key.specified & AVTAB_OP)) {
|
||||
rc = next_entry(buf32, fp, sizeof(u32));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
datum.u.data = le32_to_cpu(*buf32);
|
||||
} else {
|
||||
memset(&ops, 0, sizeof(struct avtab_operation));
|
||||
rc = next_entry(&ops.type, fp, sizeof(u8));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(ops.op.perms));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(ops.op.perms); i++)
|
||||
ops.op.perms[i] = le32_to_cpu(buf32[i]);
|
||||
datum.u.ops = &ops;
|
||||
}
|
||||
datum.data = le32_to_cpu(*buf32);
|
||||
if ((key.specified & AVTAB_TYPE) &&
|
||||
!policydb_type_isvalid(pol, datum.data)) {
|
||||
!policydb_type_isvalid(pol, datum.u.data)) {
|
||||
printk(KERN_ERR "SELinux: avtab: invalid type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -504,8 +552,9 @@ bad:
|
||||
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
|
||||
{
|
||||
__le16 buf16[4];
|
||||
__le32 buf32[1];
|
||||
__le32 buf32[ARRAY_SIZE(cur->datum.u.ops->op.perms)];
|
||||
int rc;
|
||||
unsigned int i;
|
||||
|
||||
buf16[0] = cpu_to_le16(cur->key.source_type);
|
||||
buf16[1] = cpu_to_le16(cur->key.target_type);
|
||||
@@ -514,8 +563,16 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
|
||||
rc = put_entry(buf16, sizeof(u16), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
buf32[0] = cpu_to_le32(cur->datum.data);
|
||||
rc = put_entry(buf32, sizeof(u32), 1, fp);
|
||||
|
||||
if (cur->key.specified & AVTAB_OP) {
|
||||
for (i = 0; i < ARRAY_SIZE(cur->datum.u.ops->op.perms); i++)
|
||||
buf32[i] = cpu_to_le32(cur->datum.u.ops->op.perms[i]);
|
||||
rc = put_entry(buf32, sizeof(u32),
|
||||
ARRAY_SIZE(cur->datum.u.ops->op.perms), fp);
|
||||
} else {
|
||||
buf32[0] = cpu_to_le32(cur->datum.u.data);
|
||||
rc = put_entry(buf32, sizeof(u32), 1, fp);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
@@ -548,9 +605,13 @@ void avtab_cache_init(void)
|
||||
avtab_node_cachep = kmem_cache_create("avtab_node",
|
||||
sizeof(struct avtab_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avtab_operation_cachep = kmem_cache_create("avtab_operation",
|
||||
sizeof(struct avtab_operation),
|
||||
0, SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
void avtab_cache_destroy(void)
|
||||
{
|
||||
kmem_cache_destroy(avtab_node_cachep);
|
||||
kmem_cache_destroy(avtab_operation_cachep);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#ifndef _SS_AVTAB_H_
|
||||
#define _SS_AVTAB_H_
|
||||
|
||||
#include "security.h"
|
||||
|
||||
struct avtab_key {
|
||||
u16 source_type; /* source type */
|
||||
u16 target_type; /* target type */
|
||||
@@ -35,13 +37,34 @@ struct avtab_key {
|
||||
#define AVTAB_MEMBER 0x0020
|
||||
#define AVTAB_CHANGE 0x0040
|
||||
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
|
||||
#define AVTAB_OPNUM_ALLOWED 0x0100
|
||||
#define AVTAB_OPNUM_AUDITALLOW 0x0200
|
||||
#define AVTAB_OPNUM_DONTAUDIT 0x0400
|
||||
#define AVTAB_OPNUM (AVTAB_OPNUM_ALLOWED | \
|
||||
AVTAB_OPNUM_AUDITALLOW | \
|
||||
AVTAB_OPNUM_DONTAUDIT)
|
||||
#define AVTAB_OPTYPE_ALLOWED 0x1000
|
||||
#define AVTAB_OPTYPE_AUDITALLOW 0x2000
|
||||
#define AVTAB_OPTYPE_DONTAUDIT 0x4000
|
||||
#define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | \
|
||||
AVTAB_OPTYPE_AUDITALLOW | \
|
||||
AVTAB_OPTYPE_DONTAUDIT)
|
||||
#define AVTAB_OP (AVTAB_OPNUM | AVTAB_OPTYPE)
|
||||
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
|
||||
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
|
||||
u16 specified; /* what field is specified */
|
||||
};
|
||||
|
||||
struct avtab_operation {
|
||||
u8 type;
|
||||
struct operation_perm op;
|
||||
};
|
||||
|
||||
struct avtab_datum {
|
||||
u32 data; /* access vector or type value */
|
||||
union {
|
||||
u32 data; /* access vector or type value */
|
||||
struct avtab_operation *ops; /* ioctl operations */
|
||||
} u;
|
||||
};
|
||||
|
||||
struct avtab_node {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "security.h"
|
||||
#include "conditional.h"
|
||||
#include "services.h"
|
||||
|
||||
/*
|
||||
* cond_evaluate_expr evaluates a conditional expr
|
||||
@@ -617,21 +618,39 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Determine whether additional permissions are granted by the conditional
|
||||
* av table, and if so, add them to the result
|
||||
*/
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
|
||||
|
||||
void cond_compute_operation(struct avtab *ctab, struct avtab_key *key,
|
||||
struct operation_decision *od)
|
||||
{
|
||||
struct avtab_node *node;
|
||||
|
||||
if (!ctab || !key || !avd)
|
||||
if (!ctab || !key || !od)
|
||||
return;
|
||||
|
||||
for (node = avtab_search_node(ctab, key); node;
|
||||
node = avtab_search_node_next(node, key->specified)) {
|
||||
if (node->key.specified & AVTAB_ENABLED)
|
||||
services_compute_operation_num(od, node);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
/* Determine whether additional permissions are granted by the conditional
|
||||
* av table, and if so, add them to the result
|
||||
*/
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
struct av_decision *avd, struct operation *ops)
|
||||
{
|
||||
struct avtab_node *node;
|
||||
|
||||
if (!ctab || !key || !avd || !ops)
|
||||
return;
|
||||
|
||||
for (node = avtab_search_node(ctab, key); node;
|
||||
node = avtab_search_node_next(node, key->specified)) {
|
||||
if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
|
||||
avd->allowed |= node->datum.data;
|
||||
avd->allowed |= node->datum.u.data;
|
||||
if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
|
||||
/* Since a '0' in an auditdeny mask represents a
|
||||
@@ -639,10 +658,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
|
||||
* the '&' operand to ensure that all '0's in the mask
|
||||
* are retained (much unlike the allow and auditallow cases).
|
||||
*/
|
||||
avd->auditdeny &= node->datum.data;
|
||||
avd->auditdeny &= node->datum.u.data;
|
||||
if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
|
||||
avd->auditallow |= node->datum.data;
|
||||
avd->auditallow |= node->datum.u.data;
|
||||
if ((node->key.specified & AVTAB_ENABLED) &&
|
||||
(node->key.specified & AVTAB_OP))
|
||||
services_compute_operation_type(ops, node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp);
|
||||
int cond_write_bool(void *key, void *datum, void *ptr);
|
||||
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp);
|
||||
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
|
||||
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
struct av_decision *avd, struct operation *ops);
|
||||
void cond_compute_operation(struct avtab *ctab, struct avtab_key *key,
|
||||
struct operation_decision *od);
|
||||
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
|
||||
|
||||
#endif /* _CONDITIONAL_H_ */
|
||||
|
||||
@@ -148,6 +148,11 @@ static struct policydb_compat_info policydb_compat[] = {
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_IOCTL_OPERATIONS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct policydb_compat_info *policydb_lookup_compat(int version)
|
||||
|
||||
@@ -92,9 +92,10 @@ static int context_struct_to_string(struct context *context, char **scontext,
|
||||
u32 *scontext_len);
|
||||
|
||||
static void context_struct_compute_av(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd);
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd,
|
||||
struct operation *ops);
|
||||
|
||||
struct selinux_mapping {
|
||||
u16 value; /* policy value */
|
||||
@@ -564,7 +565,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(&lo_scontext,
|
||||
tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@@ -579,7 +581,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(scontext,
|
||||
&lo_tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@@ -595,7 +598,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(&lo_scontext,
|
||||
&lo_tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@@ -611,14 +615,39 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
}
|
||||
}
|
||||
|
||||
/* flag ioctl types that have operation permissions */
|
||||
void services_compute_operation_type(
|
||||
struct operation *ops,
|
||||
struct avtab_node *node)
|
||||
{
|
||||
u8 type;
|
||||
unsigned int i;
|
||||
|
||||
if (node->key.specified & AVTAB_OPTYPE) {
|
||||
/* if allowing one or more complete types */
|
||||
for (i = 0; i < ARRAY_SIZE(ops->type); i++)
|
||||
ops->type[i] |= node->datum.u.ops->op.perms[i];
|
||||
} else {
|
||||
/* if allowing operations within a type */
|
||||
type = node->datum.u.ops->type;
|
||||
security_operation_set(ops->type, type);
|
||||
}
|
||||
|
||||
/* If no ioctl commands are allowed, ignore auditallow and auditdeny */
|
||||
if (node->key.specified & AVTAB_OPTYPE_ALLOWED ||
|
||||
node->key.specified & AVTAB_OPNUM_ALLOWED)
|
||||
ops->len = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute access vectors based on a context structure pair for
|
||||
* the permissions in a particular class.
|
||||
* Compute access vectors and operations ranges based on a context
|
||||
* structure pair for the permissions in a particular class.
|
||||
*/
|
||||
static void context_struct_compute_av(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd)
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd,
|
||||
struct operation *ops)
|
||||
{
|
||||
struct constraint_node *constraint;
|
||||
struct role_allow *ra;
|
||||
@@ -632,6 +661,10 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
avd->allowed = 0;
|
||||
avd->auditallow = 0;
|
||||
avd->auditdeny = 0xffffffff;
|
||||
if (ops) {
|
||||
memset(&ops->type, 0, sizeof(ops->type));
|
||||
ops->len = 0;
|
||||
}
|
||||
|
||||
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
||||
if (printk_ratelimit())
|
||||
@@ -646,7 +679,7 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
* this permission check, then use it.
|
||||
*/
|
||||
avkey.target_class = tclass;
|
||||
avkey.specified = AVTAB_AV;
|
||||
avkey.specified = AVTAB_AV | AVTAB_OP;
|
||||
sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1);
|
||||
BUG_ON(!sattr);
|
||||
tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1);
|
||||
@@ -659,15 +692,17 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
node;
|
||||
node = avtab_search_node_next(node, avkey.specified)) {
|
||||
if (node->key.specified == AVTAB_ALLOWED)
|
||||
avd->allowed |= node->datum.data;
|
||||
avd->allowed |= node->datum.u.data;
|
||||
else if (node->key.specified == AVTAB_AUDITALLOW)
|
||||
avd->auditallow |= node->datum.data;
|
||||
avd->auditallow |= node->datum.u.data;
|
||||
else if (node->key.specified == AVTAB_AUDITDENY)
|
||||
avd->auditdeny &= node->datum.data;
|
||||
avd->auditdeny &= node->datum.u.data;
|
||||
else if (ops && (node->key.specified & AVTAB_OP))
|
||||
services_compute_operation_type(ops, node);
|
||||
}
|
||||
|
||||
/* Check conditional av table for additional permissions */
|
||||
cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
|
||||
cond_compute_av(&policydb.te_cond_avtab, &avkey, avd, ops);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -898,13 +933,138 @@ static void avd_init(struct av_decision *avd)
|
||||
avd->flags = 0;
|
||||
}
|
||||
|
||||
void services_compute_operation_num(struct operation_decision *od,
|
||||
struct avtab_node *node)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (node->key.specified & AVTAB_OPNUM) {
|
||||
if (od->type != node->datum.u.ops->type)
|
||||
return;
|
||||
} else {
|
||||
if (!security_operation_test(node->datum.u.ops->op.perms,
|
||||
od->type))
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->key.specified == AVTAB_OPTYPE_ALLOWED) {
|
||||
od->specified |= OPERATION_ALLOWED;
|
||||
memset(od->allowed->perms, 0xff,
|
||||
sizeof(od->allowed->perms));
|
||||
} else if (node->key.specified == AVTAB_OPTYPE_AUDITALLOW) {
|
||||
od->specified |= OPERATION_AUDITALLOW;
|
||||
memset(od->auditallow->perms, 0xff,
|
||||
sizeof(od->auditallow->perms));
|
||||
} else if (node->key.specified == AVTAB_OPTYPE_DONTAUDIT) {
|
||||
od->specified |= OPERATION_DONTAUDIT;
|
||||
memset(od->dontaudit->perms, 0xff,
|
||||
sizeof(od->dontaudit->perms));
|
||||
} else if (node->key.specified == AVTAB_OPNUM_ALLOWED) {
|
||||
od->specified |= OPERATION_ALLOWED;
|
||||
for (i = 0; i < ARRAY_SIZE(od->allowed->perms); i++)
|
||||
od->allowed->perms[i] |=
|
||||
node->datum.u.ops->op.perms[i];
|
||||
} else if (node->key.specified == AVTAB_OPNUM_AUDITALLOW) {
|
||||
od->specified |= OPERATION_AUDITALLOW;
|
||||
for (i = 0; i < ARRAY_SIZE(od->auditallow->perms); i++)
|
||||
od->auditallow->perms[i] |=
|
||||
node->datum.u.ops->op.perms[i];
|
||||
} else if (node->key.specified == AVTAB_OPNUM_DONTAUDIT) {
|
||||
od->specified |= OPERATION_DONTAUDIT;
|
||||
for (i = 0; i < ARRAY_SIZE(od->dontaudit->perms); i++)
|
||||
od->dontaudit->perms[i] |=
|
||||
node->datum.u.ops->op.perms[i];
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void security_compute_operation(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
u8 type,
|
||||
struct operation_decision *od)
|
||||
{
|
||||
u16 tclass;
|
||||
struct context *scontext, *tcontext;
|
||||
struct avtab_key avkey;
|
||||
struct avtab_node *node;
|
||||
struct ebitmap *sattr, *tattr;
|
||||
struct ebitmap_node *snode, *tnode;
|
||||
unsigned int i, j;
|
||||
|
||||
od->type = type;
|
||||
od->specified = 0;
|
||||
memset(od->allowed->perms, 0, sizeof(od->allowed->perms));
|
||||
memset(od->auditallow->perms, 0, sizeof(od->auditallow->perms));
|
||||
memset(od->dontaudit->perms, 0, sizeof(od->dontaudit->perms));
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
if (!ss_initialized)
|
||||
goto allow;
|
||||
|
||||
scontext = sidtab_search(&sidtab, ssid);
|
||||
if (!scontext) {
|
||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||
__func__, ssid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tcontext = sidtab_search(&sidtab, tsid);
|
||||
if (!tcontext) {
|
||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||
__func__, tsid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tclass = unmap_class(orig_tclass);
|
||||
if (unlikely(orig_tclass && !tclass)) {
|
||||
if (policydb.allow_unknown)
|
||||
goto allow;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
||||
pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass);
|
||||
goto out;
|
||||
}
|
||||
|
||||
avkey.target_class = tclass;
|
||||
avkey.specified = AVTAB_OP;
|
||||
sattr = flex_array_get(policydb.type_attr_map_array,
|
||||
scontext->type - 1);
|
||||
BUG_ON(!sattr);
|
||||
tattr = flex_array_get(policydb.type_attr_map_array,
|
||||
tcontext->type - 1);
|
||||
BUG_ON(!tattr);
|
||||
ebitmap_for_each_positive_bit(sattr, snode, i) {
|
||||
ebitmap_for_each_positive_bit(tattr, tnode, j) {
|
||||
avkey.source_type = i + 1;
|
||||
avkey.target_type = j + 1;
|
||||
for (node = avtab_search_node(&policydb.te_avtab, &avkey);
|
||||
node;
|
||||
node = avtab_search_node_next(node, avkey.specified))
|
||||
services_compute_operation_num(od, node);
|
||||
|
||||
cond_compute_operation(&policydb.te_cond_avtab,
|
||||
&avkey, od);
|
||||
}
|
||||
}
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return;
|
||||
allow:
|
||||
memset(od->allowed->perms, 0xff, sizeof(od->allowed->perms));
|
||||
goto out;
|
||||
}
|
||||
/**
|
||||
* security_compute_av - Compute access vector decisions.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @avd: access vector decisions
|
||||
* @od: operation decisions
|
||||
*
|
||||
* Compute a set of access vector decisions based on the
|
||||
* SID pair (@ssid, @tsid) for the permissions in @tclass.
|
||||
@@ -912,13 +1072,15 @@ static void avd_init(struct av_decision *avd)
|
||||
void security_compute_av(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
struct av_decision *avd)
|
||||
struct av_decision *avd,
|
||||
struct operation *ops)
|
||||
{
|
||||
u16 tclass;
|
||||
struct context *scontext = NULL, *tcontext = NULL;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
avd_init(avd);
|
||||
ops->len = 0;
|
||||
if (!ss_initialized)
|
||||
goto allow;
|
||||
|
||||
@@ -946,7 +1108,7 @@ void security_compute_av(u32 ssid,
|
||||
goto allow;
|
||||
goto out;
|
||||
}
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd, ops);
|
||||
map_decision(orig_tclass, avd, policydb.allow_unknown);
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
@@ -992,7 +1154,7 @@ void security_compute_av_user(u32 ssid,
|
||||
goto out;
|
||||
}
|
||||
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd, NULL);
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return;
|
||||
@@ -1512,7 +1674,7 @@ static int security_compute_sid(u32 ssid,
|
||||
|
||||
if (avdatum) {
|
||||
/* Use the type from the type transition/member/change rule. */
|
||||
newcontext.type = avdatum->data;
|
||||
newcontext.type = avdatum->u.data;
|
||||
}
|
||||
|
||||
/* if we have a objname this is a file trans check so check those rules */
|
||||
|
||||
@@ -11,5 +11,11 @@
|
||||
|
||||
extern struct policydb policydb;
|
||||
|
||||
void services_compute_operation_type(struct operation *ops,
|
||||
struct avtab_node *node);
|
||||
|
||||
void services_compute_operation_num(struct operation_decision *od,
|
||||
struct avtab_node *node);
|
||||
|
||||
#endif /* _SS_SERVICES_H_ */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user