* 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:
Amit Pundir
2015-04-16 13:34:01 +05:30
38 changed files with 5281 additions and 1654 deletions

View 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.

View File

@@ -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

View File

@@ -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

View 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

File diff suppressed because it is too large Load Diff

View 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");

View 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 */

View 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,
}
} };

View File

@@ -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

View File

@@ -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);

View File

@@ -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"

View File

@@ -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
View 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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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(&current_log->misc);
vfree(current_log->buffer);
kfree(current_log->misc.name);
list_del(&current_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");

View File

@@ -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 */

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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[] =

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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_ */