Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Santhosh
kernel_cyanogen_msm8916
Commits
13d82b1b
Commit
13d82b1b
authored
10 years ago
by
Linux Build Service Account
Committed by
Gerrit - the friendly Code Review server
10 years ago
Browse files
Options
Download
Plain Diff
Merge "qmp: sphinx: Add Qualcomm Malware Protection Sphinx observer module"
parents
c57e2816
06583ee7
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1239 additions
and
0 deletions
+1239
-0
drivers/platform/msm/Kconfig
drivers/platform/msm/Kconfig
+5
-0
drivers/platform/msm/Makefile
drivers/platform/msm/Makefile
+1
-0
drivers/platform/msm/qmp_core/Makefile
drivers/platform/msm/qmp_core/Makefile
+2
-0
drivers/platform/msm/qmp_core/qmp_sphinx_logk.c
drivers/platform/msm/qmp_core/qmp_sphinx_logk.c
+734
-0
drivers/platform/msm/qmp_core/qmp_sphinx_logk.h
drivers/platform/msm/qmp_core/qmp_sphinx_logk.h
+162
-0
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.c
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.c
+157
-0
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.h
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.h
+28
-0
include/linux/qmp_sphinx_instrumentation.h
include/linux/qmp_sphinx_instrumentation.h
+150
-0
No files found.
drivers/platform/msm/Kconfig
View file @
13d82b1b
...
...
@@ -207,4 +207,9 @@ config QPNP_HAPTIC
on the Qualcomm Technologies' QPNP PMICs. It uses the android
timed-output framework.
config QMP_CORE
tristate "QMP Core"
help
This option enables Qualcomm Malware Protection Sphinx component
to log on-device software API activity.
endmenu
This diff is collapsed.
Click to expand it.
drivers/platform/msm/Makefile
View file @
13d82b1b
...
...
@@ -25,3 +25,4 @@ obj-$(CONFIG_KLM) += msm_klmd.o
obj-$(CONFIG_MSM_SPSS)
+=
spss.o
obj-$(CONFIG_MSM_UIM_HSL)
+=
uim_hs_lite.o
obj-$(CONFIG_QPNP_HAPTIC)
+=
qpnp-haptic.o
obj-$(CONFIG_QMP_CORE)
+=
qmp_core/
\ No newline at end of file
This diff is collapsed.
Click to expand it.
drivers/platform/msm/qmp_core/Makefile
0 → 100644
View file @
13d82b1b
obj-$(CONFIG_QMP_CORE)
+=
qmp_core.o
qmp_core-objs
:=
qmp_sphinx_logk.o qmp_sphinx_ringbuf.o
This diff is collapsed.
Click to expand it.
drivers/platform/msm/qmp_core/qmp_sphinx_logk.c
0 → 100644
View file @
13d82b1b
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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) "qmp_sphinx: %s: " fmt, __func__
#include "qmp_sphinx_logk.h"
#include "qmp_sphinx_ringbuf.h"
#ifndef VM_RESERVED
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
#define MASK_BUFFER_SIZE 256
#define FOUR_MB 4
#define USER_APP_START_UID 10000
#define YEAR_BASE 1900
static
struct
qmp_sphinx_logk_dev
*
slogk_dev
;
static
unsigned
int
ring_sz
=
FOUR_MB
;
/*
* default is besteffort, apps do not get blocked
*/
static
unsigned
int
block_apps
;
/*
* When this flag is turned on,
* kmalloc should be used for ring buf allocation
* otherwise it is vmalloc.
* default is to use vmalloc
* kmalloc has a limit of 4MB
*/
unsigned
int
kmalloc_flag
;
static
struct
class
*
cl
;
static
rwlock_t
filter_lock
;
static
struct
qmp_sphinx_source_mask
*
pmask
;
static
unsigned
int
num_sources
;
static
long
qmp_sphinx_logk_reserve_rdblks
(
struct
qmp_sphinx_logk_dev
*
sdev
,
unsigned
long
arg
);
static
long
qmp_sphinx_logk_set_mask
(
unsigned
long
arg
);
static
long
qmp_sphinx_logk_set_mapping
(
unsigned
long
arg
);
static
long
qmp_sphinx_logk_check_filter
(
unsigned
long
arg
);
void
*
(
*
qmp_sphinx_logk_kernel_begin
)(
char
**
buf
);
void
(
*
qmp_sphinx_logk_kernel_end
)
(
void
*
blck
);
/*
* the last param is the permission bits *
* kernel logging is done in three steps:
* (1) fetch a block, fill everything except payload.
* (2) return payload pointer to the caller.
* (3) caller fills its data directly into the payload area.
* (4) caller invoked finish_record(), to finish writing.
*/
void
*
qmp_sphinx_logk_kernel_start_record
(
char
**
buf
)
{
struct
qmp_sphinx_logk_blk
*
blk
;
struct
timespec
now
;
struct
tm
ts
;
int
idx
;
int
ret
;
DEFINE_WAIT
(
write_wait
);
ret
=
0
;
idx
=
0
;
now
=
current_kernel_time
();
blk
=
ringbuf_fetch_wr_block
(
slogk_dev
);
if
(
!
blk
)
{
/*
* there is no blk to write
* if block_apps == 0; quietly return
*/
if
(
!
block_apps
)
{
*
buf
=
NULL
;
return
NULL
;
}
/*else wait for the blks to be available*/
while
(
1
)
{
mutex_lock
(
&
slogk_dev
->
lock
);
prepare_to_wait
(
&
slogk_dev
->
writers_wq
,
&
write_wait
,
TASK_INTERRUPTIBLE
);
ret
=
(
slogk_dev
->
num_write_avail_blks
<=
0
);
if
(
!
ret
)
{
/* don't have to wait*/
break
;
}
mutex_unlock
(
&
slogk_dev
->
lock
);
if
(
signal_pending
(
current
))
{
ret
=
-
EINTR
;
break
;
}
schedule
();
}
finish_wait
(
&
slogk_dev
->
writers_wq
,
&
write_wait
);
if
(
ret
)
{
mutex_unlock
(
&
slogk_dev
->
lock
);
return
NULL
;
}
idx
=
slogk_dev
->
write_idx
;
slogk_dev
->
write_idx
=
(
slogk_dev
->
write_idx
+
1
)
%
slogk_dev
->
num_tot_blks
;
slogk_dev
->
num_write_avail_blks
--
;
slogk_dev
->
num_write_in_prog_blks
++
;
slogk_dev
->
num_writers
++
;
blk
=
&
slogk_dev
->
ring
[
idx
];
/*mark block invalid*/
blk
->
status
=
0x0
;
mutex_unlock
(
&
slogk_dev
->
lock
);
}
blk
->
version
=
OBSERVER_VERSION
;
blk
->
pid
=
current
->
tgid
;
blk
->
tid
=
current
->
pid
;
blk
->
uid
=
current_uid
();
blk
->
sec
=
now
.
tv_sec
;
blk
->
nsec
=
now
.
tv_nsec
;
strlcpy
(
blk
->
appname
,
current
->
comm
,
TASK_COMM_LEN
);
time_to_tm
(
now
.
tv_sec
,
0
,
&
ts
);
ts
.
tm_year
+=
YEAR_BASE
;
ts
.
tm_mon
+=
1
;
snprintf
(
blk
->
ts
,
TS_SIZE
,
"%04ld-%02d-%02d %02d:%02d:%02d"
,
ts
.
tm_year
,
ts
.
tm_mon
,
ts
.
tm_mday
,
ts
.
tm_hour
,
ts
.
tm_min
,
ts
.
tm_sec
);
*
buf
=
blk
->
msg
;
return
blk
;
}
void
qmp_sphinx_logk_kernel_end_record
(
void
*
blck
)
{
struct
qmp_sphinx_logk_blk
*
blk
=
(
struct
qmp_sphinx_logk_blk
*
)
blck
;
if
(
blk
)
{
blk
->
len
=
strlen
(
blk
->
msg
);
/*update status at the very end*/
blk
->
status
|=
0x1
;
ringbuf_finish_writer
(
slogk_dev
);
}
}
/*
* get_uid_from_message_for_system_event() - helper function to get the
* uid of the actual app that is changing the state and updating it
* accordingly rather than with the system UID = 1000
* NOTE: Not a very efficient implementation. This does a N*8 character
* comparisons everytime a message with UID less than 10000 is seen
*/
int
get_uid_from_message_for_system_event
(
const
char
*
buffer
)
{
char
asciiuid
[
6
];
long
appuid
=
0
;
int
aindex
=
0
;
char
*
comparator_string
=
"app_uid="
;
int
ret
=
0
;
char
*
p1
=
(
char
*
)
buffer
;
while
(
*
p1
)
{
char
*
p1begin
=
p1
;
char
*
p2
=
(
char
*
)
comparator_string
;
aindex
=
0
;
while
(
*
p1
&&
*
p2
&&
*
p1
==
*
p2
)
{
p1
++
;
p2
++
;
}
if
(
*
p2
==
'\0'
)
{
while
(
*
p1
!=
','
)
{
asciiuid
[
aindex
++
]
=
*
p1
;
p1
++
;
}
asciiuid
[
aindex
]
=
'\0'
;
/*
* now get the integer value of this ascii
* string number
*/
ret
=
kstrtol
(
asciiuid
,
10
,
&
appuid
);
if
(
ret
!=
0
)
{
pr_err
(
"failed in the kstrtol function uid:%s
\n
"
,
asciiuid
);
return
ret
;
}
else
{
return
(
int
)(
appuid
);
}
}
p1
=
p1begin
+
1
;
}
return
-
EPERM
;
}
static
int
qmp_sphinx_logk_usr_record
(
const
char
__user
*
buf
,
size_t
count
)
{
struct
qmp_sphinx_logk_blk
*
blk
;
struct
timespec
now
;
struct
tm
ts
;
int
idx
,
ret
;
int
currentuid
;
int
parsedcurrentuid
;
DEFINE_WAIT
(
write_wait
);
idx
=
ret
=
0
;
now
=
current_kernel_time
();
blk
=
ringbuf_fetch_wr_block
(
slogk_dev
);
if
(
!
blk
)
{
/*just drop this log*/
if
(
!
block_apps
)
return
0
;
while
(
1
)
{
mutex_lock
(
&
slogk_dev
->
lock
);
prepare_to_wait
(
&
slogk_dev
->
writers_wq
,
&
write_wait
,
TASK_INTERRUPTIBLE
);
ret
=
(
slogk_dev
->
num_write_avail_blks
<=
0
);
if
(
!
ret
)
{
/*don't have to wait*/
break
;
}
mutex_unlock
(
&
slogk_dev
->
lock
);
if
(
signal_pending
(
current
))
{
ret
=
-
EINTR
;
break
;
}
schedule
();
}
finish_wait
(
&
slogk_dev
->
writers_wq
,
&
write_wait
);
if
(
ret
)
{
mutex_unlock
(
&
slogk_dev
->
lock
);
return
-
EINTR
;
}
idx
=
slogk_dev
->
write_idx
;
slogk_dev
->
write_idx
=
(
slogk_dev
->
write_idx
+
1
)
%
slogk_dev
->
num_tot_blks
;
slogk_dev
->
num_write_avail_blks
--
;
slogk_dev
->
num_write_in_prog_blks
++
;
slogk_dev
->
num_writers
++
;
blk
=
&
slogk_dev
->
ring
[
idx
];
/*mark block invalid*/
blk
->
status
=
0x0
;
mutex_unlock
(
&
slogk_dev
->
lock
);
}
if
(
count
>
BLK_MAX_MSG_SZ
-
1
)
count
=
BLK_MAX_MSG_SZ
-
1
;
if
(
copy_from_user
(
blk
->
msg
,
buf
,
count
)
!=
0
)
ret
=
-
EFAULT
;
blk
->
msg
[
count
]
=
'\0'
;
blk
->
len
=
count
;
blk
->
pid
=
current
->
tgid
;
currentuid
=
current_uid
();
if
(
currentuid
<=
USER_APP_START_UID
)
{
parsedcurrentuid
=
get_uid_from_message_for_system_event
(
blk
->
msg
);
if
(
parsedcurrentuid
!=
-
EPERM
)
blk
->
uid
=
parsedcurrentuid
;
else
blk
->
uid
=
currentuid
;
}
else
blk
->
uid
=
currentuid
;
blk
->
tid
=
current
->
pid
;
blk
->
sec
=
now
.
tv_sec
;
blk
->
nsec
=
now
.
tv_nsec
;
time_to_tm
(
now
.
tv_sec
,
0
,
&
ts
);
ts
.
tm_year
+=
YEAR_BASE
;
ts
.
tm_mon
+=
1
;
snprintf
(
blk
->
ts
,
TS_SIZE
,
"%02ld-%02d-%02d %02d:%02d:%02d"
,
ts
.
tm_year
,
ts
.
tm_mon
,
ts
.
tm_mday
,
ts
.
tm_hour
,
ts
.
tm_min
,
ts
.
tm_sec
);
strlcpy
(
blk
->
appname
,
current
->
comm
,
TASK_COMM_LEN
);
/*update status at the very end*/
blk
->
status
|=
0x1
;
ringbuf_finish_writer
(
slogk_dev
);
return
ret
;
}
static
void
qmp_sphinx_logk_attach
(
void
)
{
qmp_sphinx_logk_kernel_end
=
qmp_sphinx_logk_kernel_end_record
;
qmp_sphinx_logk_kernel_begin
=
qmp_sphinx_logk_kernel_start_record
;
}
static
void
qmp_sphinx_logk_detach
(
void
)
{
qmp_sphinx_logk_kernel_begin
=
NULL
;
qmp_sphinx_logk_kernel_end
=
NULL
;
}
static
ssize_t
qmp_sphinx_logk_write
(
struct
file
*
file
,
const
char
__user
*
buf
,
size_t
count
,
loff_t
*
ppos
)
{
return
qmp_sphinx_logk_usr_record
(
buf
,
count
);
}
static
int
qmp_sphinx_logk_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
int
ret
;
/*disallow seeks on this file*/
ret
=
nonseekable_open
(
inode
,
filp
);
if
(
ret
)
{
pr_err
(
"ret= %d
\n
"
,
ret
);
return
ret
;
}
slogk_dev
->
minor
=
iminor
(
inode
);
filp
->
private_data
=
slogk_dev
;
return
0
;
}
static
bool
qmp_sphinx_logk_get_bit_from_vector
(
__u8
*
pVec
,
__u32
index
)
{
unsigned
int
byte_num
=
index
/
8
;
unsigned
int
bit_num
=
index
%
8
;
unsigned
char
byte
=
pVec
[
byte_num
];
return
!
(
byte
&
(
1
<<
bit_num
));
}
static
long
qmp_sphinx_logk_ioctl
(
struct
file
*
filp
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
qmp_sphinx_logk_dev
*
sdev
;
int
ret
;
sdev
=
(
struct
qmp_sphinx_logk_dev
*
)
filp
->
private_data
;
if
(
cmd
==
QMP_SPHINX_CMD_RESERVE_RDBLKS
)
{
return
qmp_sphinx_logk_reserve_rdblks
(
sdev
,
arg
);
}
else
if
(
cmd
==
QMP_SPHINX_CMD_RELEASE_RDBLKS
)
{
mutex_lock
(
&
sdev
->
lock
);
sdev
->
read_idx
=
(
sdev
->
read_idx
+
sdev
->
num_read_in_prog_blks
)
%
sdev
->
num_tot_blks
;
sdev
->
num_write_avail_blks
+=
sdev
->
num_read_in_prog_blks
;
ret
=
sdev
->
num_read_in_prog_blks
;
sdev
->
num_read_in_prog_blks
=
0
;
/*wake up any waiting writers*/
mutex_unlock
(
&
sdev
->
lock
);
if
(
ret
&&
block_apps
)
wake_up_interruptible
(
&
sdev
->
writers_wq
);
}
else
if
(
cmd
==
QMP_SPHINX_CMD_GET_RINGSZ
)
{
if
(
copy_to_user
((
unsigned
int
*
)
arg
,
&
sdev
->
ring_sz
,
sizeof
(
unsigned
int
)))
return
-
EFAULT
;
}
else
if
(
cmd
==
QMP_SPHINX_CMD_GET_BLKSZ
)
{
if
(
copy_to_user
((
unsigned
int
*
)
arg
,
&
sdev
->
blk_sz
,
sizeof
(
unsigned
int
)))
return
-
EFAULT
;
}
else
if
(
QMP_SPHINX_CMD_SET_MASK
==
cmd
)
{
return
qmp_sphinx_logk_set_mask
(
arg
);
}
else
if
(
QMP_SPHINX_CMD_SET_MAPPING
==
cmd
)
{
return
qmp_sphinx_logk_set_mapping
(
arg
);
}
else
if
(
QMP_SPHINX_CMD_CHECK_FILTER
==
cmd
)
{
return
qmp_sphinx_logk_check_filter
(
arg
);
}
else
{
pr_err
(
"Invalid Request %X
\n
"
,
cmd
);
return
-
ENOIOCTLCMD
;
}
return
0
;
}
static
long
qmp_sphinx_logk_reserve_rdblks
(
struct
qmp_sphinx_logk_dev
*
sdev
,
unsigned
long
arg
)
{
int
ret
;
struct
read_range
rrange
;
DEFINE_WAIT
(
read_wait
);
mutex_lock
(
&
sdev
->
lock
);
if
(
sdev
->
num_writers
>
0
||
sdev
->
num_read_avail_blks
<=
0
)
{
ret
=
-
EPERM
;
pr_debug
(
"(reserve): blocking, cannot read.
\n
"
);
pr_debug
(
"num_writers=%d num_read_avail_blks=%d
\n
"
,
sdev
->
num_writers
,
sdev
->
num_read_avail_blks
);
mutex_unlock
(
&
sdev
->
lock
);
/*
* unlock the device
* wait on a wait queue
* after wait, grab the dev lock again
*/
while
(
1
)
{
mutex_lock
(
&
sdev
->
lock
);
prepare_to_wait
(
&
sdev
->
readers_wq
,
&
read_wait
,
TASK_INTERRUPTIBLE
);
ret
=
(
sdev
->
num_writers
>
0
||
sdev
->
num_read_avail_blks
<=
0
);
if
(
!
ret
)
{
/*don't have to wait*/
break
;
}
mutex_unlock
(
&
sdev
->
lock
);
if
(
signal_pending
(
current
))
{
ret
=
-
EINTR
;
break
;
}
schedule
();
}
finish_wait
(
&
sdev
->
readers_wq
,
&
read_wait
);
if
(
ret
)
{
mutex_unlock
(
&
slogk_dev
->
lock
);
return
-
EINTR
;
}
}
/*sdev->lock is held at this point*/
sdev
->
num_read_in_prog_blks
=
sdev
->
num_read_avail_blks
;
sdev
->
num_read_avail_blks
=
0
;
rrange
.
start_idx
=
sdev
->
read_idx
;
rrange
.
num
=
sdev
->
num_read_in_prog_blks
;
mutex_unlock
(
&
sdev
->
lock
);
if
(
copy_to_user
((
unsigned
int
*
)
arg
,
&
rrange
,
sizeof
(
struct
read_range
)))
return
-
EFAULT
;
return
0
;
}
static
long
qmp_sphinx_logk_set_mask
(
unsigned
long
arg
)
{
__u8
buffer
[
256
];
int
i
;
unsigned
int
num_elements
;
if
(
copy_from_user
(
&
num_elements
,
(
unsigned
int
__user
*
)
arg
,
sizeof
(
unsigned
int
)))
return
-
EFAULT
;
if
(
0
==
num_sources
)
return
-
EINVAL
;
if
(
num_elements
==
0
||
MASK_BUFFER_SIZE
<
DIV_ROUND_UP
(
num_sources
,
8
))
return
-
EINVAL
;
if
(
copy_from_user
(
buffer
,
(
__u8
*
)
arg
,
DIV_ROUND_UP
(
num_sources
,
8
)))
return
-
EFAULT
;
write_lock
(
&
filter_lock
);
if
(
num_elements
!=
num_sources
)
{
write_unlock
(
&
filter_lock
);
return
-
EPERM
;
}
for
(
i
=
0
;
i
<
num_sources
;
i
++
)
{
pmask
[
i
].
isOn
=
qmp_sphinx_logk_get_bit_from_vector
(
(
__u8
*
)
buffer
,
i
);
}
write_unlock
(
&
filter_lock
);
return
0
;
}
static
long
qmp_sphinx_logk_set_mapping
(
unsigned
long
arg
)
{
__u32
num_elements
;
__u32
*
pbuffer
;
int
i
;
struct
qmp_sphinx_source_mask
*
pnewmask
;
if
(
copy_from_user
(
&
num_elements
,
(
__u32
__user
*
)
arg
,
sizeof
(
__u32
)))
return
-
EFAULT
;
if
(
0
==
num_elements
)
return
-
EFAULT
;
if
(
NULL
!=
pmask
)
{
/*
* Mask is getting set again.
* qmp_sphinx_core was probably restarted.
*/
struct
qmp_sphinx_source_mask
*
ptempmask
;
write_lock
(
&
filter_lock
);
num_sources
=
0
;
ptempmask
=
pmask
;
pmask
=
NULL
;
write_unlock
(
&
filter_lock
);
kfree
(
ptempmask
);
}
pbuffer
=
kmalloc
(
sizeof
(
struct
qmp_sphinx_source_mask
)
*
num_elements
,
GFP_KERNEL
);
if
(
NULL
==
pbuffer
)
return
-
ENOMEM
;
/*
* Use our new table as scratch space for now.
* We copy an ordered list of hash values into our buffer.
*/
if
(
copy_from_user
(
pbuffer
,
&
((
__u32
__user
*
)
arg
)[
1
],
num_elements
*
sizeof
(
unsigned
int
)))
{
kfree
(
pbuffer
);
return
-
EFAULT
;
}
/*
* We arrange the user data into a more usable form.
* This is done in-place.
*/
pnewmask
=
(
struct
qmp_sphinx_source_mask
*
)
pbuffer
;
for
(
i
=
num_elements
-
1
;
i
>=
0
;
i
--
)
{
pnewmask
[
i
].
hash
=
pbuffer
[
i
];
/* Observer is off by default*/
pnewmask
[
i
].
isOn
=
0
;
}
write_lock
(
&
filter_lock
);
pmask
=
pnewmask
;
num_sources
=
num_elements
;
write_unlock
(
&
filter_lock
);
return
0
;
}
static
long
qmp_sphinx_logk_check_filter
(
unsigned
long
arg
)
{
int
i
;
unsigned
int
hash
=
(
unsigned
int
)
arg
;
/*
* This lock may be a bit long.
* If it is a problem, it can be fixed.
*/
read_lock
(
&
filter_lock
);
for
(
i
=
0
;
i
<
num_sources
;
i
++
)
{
if
(
hash
==
pmask
[
i
].
hash
)
{
int
result
=
pmask
[
i
].
isOn
;
read_unlock
(
&
filter_lock
);
return
result
;
}
}
read_unlock
(
&
filter_lock
);
return
0
;
}
static
int
qmp_sphinx_logk_mmap
(
struct
file
*
filp
,
struct
vm_area_struct
*
vma
)
{
int
ret
;
char
*
vptr
;
unsigned
long
length
,
pfn
;
unsigned
long
start
=
vma
->
vm_start
;
length
=
vma
->
vm_end
-
vma
->
vm_start
;
if
(
length
>
(
unsigned
long
)
slogk_dev
->
ring_sz
)
{
pr_err
(
"len check failed
\n
"
);
return
-
EIO
;
}
vma
->
vm_flags
|=
VM_RESERVED
|
VM_SHARED
;
vptr
=
(
char
*
)
slogk_dev
->
ring
;
ret
=
0
;
if
(
kmalloc_flag
)
{
ret
=
remap_pfn_range
(
vma
,
start
,
virt_to_phys
((
void
*
)
((
unsigned
long
)
slogk_dev
->
ring
))
>>
PAGE_SHIFT
,
length
,
vma
->
vm_page_prot
);
if
(
ret
!=
0
)
{
pr_err
(
"remap_pfn_range() fails with ret = %d
\n
"
,
ret
);
return
-
EAGAIN
;
}
}
else
{
while
(
length
>
0
)
{
pfn
=
vmalloc_to_pfn
(
vptr
);
ret
=
remap_pfn_range
(
vma
,
start
,
pfn
,
PAGE_SIZE
,
vma
->
vm_page_prot
);
if
(
ret
<
0
)
{
pr_err
(
"remap_pfn_range() fails with ret = %d
\n
"
,
ret
);
return
ret
;
}
start
+=
PAGE_SIZE
;
vptr
+=
PAGE_SIZE
;
length
-=
PAGE_SIZE
;
}
}
return
0
;
}
static
const
struct
file_operations
qmp_sphinx_logk_fops
=
{
.
write
=
qmp_sphinx_logk_write
,
.
open
=
qmp_sphinx_logk_open
,
.
unlocked_ioctl
=
qmp_sphinx_logk_ioctl
,
.
compat_ioctl
=
qmp_sphinx_logk_ioctl
,
.
mmap
=
qmp_sphinx_logk_mmap
,
};
__init
int
qmp_sphinx_logk_init
(
void
)
{
int
ret
;
int
devno
=
0
;
num_sources
=
0
;
kmalloc_flag
=
0
;
block_apps
=
0
;
pmask
=
NULL
;
if
(
kmalloc_flag
&&
ring_sz
>
FOUR_MB
)
{
pr_err
(
"kmalloc cannot allocate > 4MB
\n
"
);
return
-
ENOMEM
;
}
ring_sz
=
ring_sz
*
SZ_1M
;
if
(
ring_sz
<=
0
)
{
pr_err
(
"Too small a ring_sz=%d
\n
"
,
ring_sz
);
return
-
EINVAL
;
}
slogk_dev
=
kmalloc
(
sizeof
(
*
slogk_dev
),
GFP_KERNEL
);
if
(
!
slogk_dev
)
{
pr_err
(
"kmalloc failure!
\n
"
);
return
-
ENOMEM
;
}
slogk_dev
->
ring_sz
=
ring_sz
;
slogk_dev
->
blk_sz
=
sizeof
(
struct
qmp_sphinx_logk_blk
);
/*intialize ping-pong buffers*/
ret
=
ringbuf_init
(
slogk_dev
);
if
(
ret
<
0
)
{
pr_err
(
"Init Failed, ret = %d
\n
"
,
ret
);
goto
pingpong_fail
;
}
ret
=
alloc_chrdev_region
(
&
devno
,
0
,
qmp_sphinx_LOGK_NUM_DEVS
,
qmp_sphinx_LOGK_DEV_NAME
);
if
(
ret
<
0
)
{
pr_err
(
"alloc_chrdev_region failed with ret = %d
\n
"
,
ret
);
goto
register_fail
;
}
slogk_dev
->
major
=
MAJOR
(
devno
);
pr_debug
(
"logk: major# = %d
\n
"
,
slogk_dev
->
major
);
cl
=
class_create
(
THIS_MODULE
,
qmp_sphinx_LOGK_DEV_NAME
);
if
(
cl
==
NULL
)
{
pr_err
(
"class create failed"
);
goto
cdev_fail
;
}
if
(
device_create
(
cl
,
NULL
,
devno
,
NULL
,
qmp_sphinx_LOGK_DEV_NAME
)
==
NULL
)
{
pr_err
(
"device create failed"
);
goto
class_destroy_fail
;
}
cdev_init
(
&
(
slogk_dev
->
cdev
),
&
qmp_sphinx_logk_fops
);
slogk_dev
->
cdev
.
owner
=
THIS_MODULE
;
ret
=
cdev_add
(
&
(
slogk_dev
->
cdev
),
MKDEV
(
slogk_dev
->
major
,
0
),
1
);
if
(
ret
)
{
pr_err
(
"cdev_add failed with ret = %d"
,
ret
);
goto
class_destroy_fail
;
}
qmp_sphinx_logk_attach
();
mutex_init
(
&
slogk_dev
->
lock
);
init_waitqueue_head
(
&
slogk_dev
->
readers_wq
);
init_waitqueue_head
(
&
slogk_dev
->
writers_wq
);
rwlock_init
(
&
filter_lock
);
return
0
;
class_destroy_fail:
class_destroy
(
cl
);
cdev_fail:
unregister_chrdev_region
(
devno
,
qmp_sphinx_LOGK_NUM_DEVS
);
register_fail:
ringbuf_cleanup
(
slogk_dev
);
pingpong_fail:
kfree
(
slogk_dev
);
return
-
EPERM
;
}
__exit
void
qmp_sphinx_logk_cleanup
(
void
)
{
dev_t
devno
=
MKDEV
(
slogk_dev
->
major
,
slogk_dev
->
minor
);
qmp_sphinx_logk_detach
();
cdev_del
(
&
slogk_dev
->
cdev
);
unregister_chrdev_region
(
devno
,
qmp_sphinx_LOGK_NUM_DEVS
);
ringbuf_cleanup
(
slogk_dev
);
kfree
(
slogk_dev
);
if
(
NULL
!=
pmask
)
{
kfree
(
pmask
);
pmask
=
NULL
;
}
}
module_init
(
qmp_sphinx_logk_init
);
module_exit
(
qmp_sphinx_logk_cleanup
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_DESCRIPTION
(
"qmp_sphinx Observer"
);
This diff is collapsed.
Click to expand it.
drivers/platform/msm/qmp_core/qmp_sphinx_logk.h
0 → 100644
View file @
13d82b1b
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 __qmp_sphinx_LOGK_H__
#define __qmp_sphinx_LOGK_H__
#define OBSERVER_VERSION 0x01
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <asm/ioctls.h>
#define qmp_sphinx_LOGK_NUM_DEVS 1
#define qmp_sphinx_LOGK_DEV_NAME "qlog"
/*
* The logcat driver on Android uses four 256k ring buffers
* here, we use two ring buffers of the same size.
* we think this is reasonable
*/
#define FULL_BUF_SIZE (64 * 1024 * 1024)
#define HALF_BUF_SIZE (32 * 1024 * 1024)
#define FULL_BLOCKS (8 * 1024)
#define HALF_BLOCKS (4 * 1024)
#define TS_SIZE 20
#define BLK_SIZE 256
#define BLK_HDR_SIZE 64
#define BLK_MAX_MSG_SZ (BLK_SIZE - BLK_HDR_SIZE)
#define READER_NOT_READY 0
#define READER_READY 1
#define MAGIC 'z'
#define QMP_SPHINX_CMD_RESERVE_RDBLKS _IOR(MAGIC, 1, int)
#define QMP_SPHINX_CMD_RELEASE_RDBLKS _IO(MAGIC, 2)
#define QMP_SPHINX_CMD_GET_RINGSZ _IOR(MAGIC, 3, int)
#define QMP_SPHINX_CMD_GET_BLKSZ _IOR(MAGIC, 4, int)
#define QMP_SPHINX_CMD_SET_MASK _IO(MAGIC, 5)
#define QMP_SPHINX_CMD_SET_MAPPING _IO(MAGIC, 6)
#define QMP_SPHINX_CMD_CHECK_FILTER _IOR(MAGIC, 7, int)
struct
read_range
{
int
start_idx
;
int
num
;
};
struct
qmp_sphinx_logk_dev
{
unsigned
int
major
;
unsigned
int
minor
;
struct
cdev
cdev
;
struct
class
*
cls
;
/*the full buffer*/
struct
qmp_sphinx_logk_blk
*
ring
;
/*an array of blks*/
unsigned
int
ring_sz
;
unsigned
int
blk_sz
;
int
num_tot_blks
;
int
num_write_avail_blks
;
int
num_write_in_prog_blks
;
int
num_read_avail_blks
;
int
num_read_in_prog_blks
;
int
num_writers
;
/*
* there is always one reader
* which is the observer daemon
* therefore there is no necessity
* for num_readers variable
*/
/*
* read_idx and write_idx loop through from zero to ring_sz,
* and then back to zero in a circle, as they advance
* based on the reader's and writers' accesses
*/
int
read_idx
;
int
write_idx
;
/*
* wait queues
* readers_wq: implement wait for readers
* writers_wq: implement wait for writers
*
* whether writers are blocked or not is driven by the policy:
* case 1: (best_effort_logging == 1)
* writers are not blocked, and
* when there is no mem in the ring to store logs,
* the logs are simply dropped.
* case 2: (best_effort_logging == 0)
* when there is no mem in the ring to store logs,
* the process gets blocked until there is space.
*/
wait_queue_head_t
readers_wq
;
wait_queue_head_t
writers_wq
;
/*
* protects everything in the device
* including ring buffer and all the num_ variables
* spinlock_t lock;
*/
struct
mutex
lock
;
};
struct
qmp_sphinx_logk_blk
{
/*bits: 0->valid/invalid; 1-7: unused as of now!*/
__u8
status
;
__u16
len
;
__u8
version
;
__s32
pid
;
__s32
uid
;
__s32
tid
;
__s32
sec
;
__s32
nsec
;
char
ts
[
TS_SIZE
];
char
appname
[
TASK_COMM_LEN
];
/*
* HDR = 24 bytes + TS_SIZE(20) | TASK_COMM_LEN(16) (total = 60)
* msg = BLK_SIZE(256) - BLK_HDR_SIZE(60) = 196
*/
char
msg
[
BLK_SIZE
-
BLK_HDR_SIZE
];
/* the entry's payload */
};
extern
unsigned
int
kmalloc_flag
;
struct
qmp_sphinx_source_mask
{
__u32
hash
;
bool
isOn
;
};
#endif
This diff is collapsed.
Click to expand it.
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.c
0 → 100644
View file @
13d82b1b
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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) "qmp_sphinx: %s: " fmt, __func__
#include "qmp_sphinx_logk.h"
#include "qmp_sphinx_ringbuf.h"
/*initial function no need to hold ring_lock*/
int
ringbuf_init
(
struct
qmp_sphinx_logk_dev
*
sdev
)
{
char
*
buf
;
unsigned
long
virt_addr
;
if
(
kmalloc_flag
)
{
sdev
->
ring
=
kmalloc
(
sdev
->
ring_sz
,
GFP_KERNEL
);
if
(
sdev
->
ring
==
NULL
)
{
pr_err
(
"kmalloc failed, ring_sz= %d
\n
"
,
sdev
->
ring_sz
);
return
-
ENOMEM
;
}
buf
=
(
char
*
)
sdev
->
ring
;
/*reserve kmalloc memory as pages to make them remapable*/
for
(
virt_addr
=
(
unsigned
long
)
buf
;
virt_addr
<
(
unsigned
long
)
buf
+
sdev
->
ring_sz
;
virt_addr
+=
PAGE_SIZE
)
{
SetPageReserved
(
virt_to_page
((
virt_addr
)));
}
}
else
{
sdev
->
ring
=
vmalloc
(
sdev
->
ring_sz
);
if
(
sdev
->
ring
==
NULL
)
{
pr_err
(
"vmalloc failed, ring_sz = %d
\n
"
,
sdev
->
ring_sz
);
return
-
ENOMEM
;
}
buf
=
(
char
*
)
sdev
->
ring
;
/*reserve vmalloc memory as pages to make them remapable*/
for
(
virt_addr
=
(
unsigned
long
)
buf
;
virt_addr
<
(
unsigned
long
)
buf
+
sdev
->
ring_sz
;
virt_addr
+=
PAGE_SIZE
)
{
SetPageReserved
(
vmalloc_to_page
(
(
unsigned
long
*
)
virt_addr
));
}
}
memset
(
sdev
->
ring
,
0
,
sdev
->
ring_sz
);
sdev
->
num_tot_blks
=
(
sdev
->
ring_sz
/
BLK_SIZE
);
sdev
->
num_writers
=
0
;
sdev
->
write_idx
=
0
;
sdev
->
read_idx
=
0
;
sdev
->
num_write_avail_blks
=
sdev
->
num_tot_blks
;
/*no. of blocks available for write*/
sdev
->
num_write_in_prog_blks
=
0
;
/*no. of blocks held by writers to perform writes*/
sdev
->
num_read_avail_blks
=
0
;
/*no. of blocks ready for read*/
sdev
->
num_read_in_prog_blks
=
0
;
/*no. of blocks held by the reader to perform read*/
return
0
;
}
void
ringbuf_cleanup
(
struct
qmp_sphinx_logk_dev
*
sdev
)
{
unsigned
long
virt_addr
;
if
(
kmalloc_flag
)
{
for
(
virt_addr
=
(
unsigned
long
)
sdev
->
ring
;
virt_addr
<
(
unsigned
long
)
sdev
->
ring
+
sdev
->
ring_sz
;
virt_addr
+=
PAGE_SIZE
)
{
/*clear all pages*/
ClearPageReserved
(
virt_to_page
((
unsigned
long
*
)
virt_addr
));
}
kfree
(
sdev
->
ring
);
}
else
{
for
(
virt_addr
=
(
unsigned
long
)
sdev
->
ring
;
virt_addr
<
(
unsigned
long
)
sdev
->
ring
+
sdev
->
ring_sz
;
virt_addr
+=
PAGE_SIZE
)
{
/*clear all pages*/
ClearPageReserved
(
vmalloc_to_page
((
unsigned
long
*
)
virt_addr
));
}
vfree
(
sdev
->
ring
);
}
}
struct
qmp_sphinx_logk_blk
*
ringbuf_fetch_wr_block
(
struct
qmp_sphinx_logk_dev
*
sdev
)
{
struct
qmp_sphinx_logk_blk
*
blk
=
NULL
;
int
idx
;
mutex_lock
(
&
sdev
->
lock
);
if
(
sdev
->
num_write_avail_blks
==
0
)
{
idx
=
-
1
;
mutex_unlock
(
&
sdev
->
lock
);
return
blk
;
}
idx
=
sdev
->
write_idx
;
sdev
->
write_idx
=
(
sdev
->
write_idx
+
1
)
%
sdev
->
num_tot_blks
;
sdev
->
num_write_avail_blks
--
;
sdev
->
num_write_in_prog_blks
++
;
sdev
->
num_writers
++
;
blk
=
&
sdev
->
ring
[
idx
];
blk
->
status
=
0x0
;
mutex_unlock
(
&
sdev
->
lock
);
return
blk
;
}
void
ringbuf_finish_writer
(
struct
qmp_sphinx_logk_dev
*
sdev
)
{
/*
* finish writing...
* the calling process will no longer access this block.
*/
mutex_lock
(
&
sdev
->
lock
);
sdev
->
num_writers
--
;
sdev
->
num_write_in_prog_blks
--
;
sdev
->
num_read_avail_blks
++
;
/*wake up any readers*/
if
(
sdev
->
num_writers
==
0
)
wake_up_interruptible
(
&
sdev
->
readers_wq
);
mutex_unlock
(
&
sdev
->
lock
);
}
int
ringbuf_count_marked
(
struct
qmp_sphinx_logk_dev
*
sdev
)
{
int
i
;
unsigned
int
marked
;
mutex_lock
(
&
sdev
->
lock
);
for
(
marked
=
0
,
i
=
0
;
i
<
sdev
->
num_tot_blks
;
i
++
)
if
(
sdev
->
ring
[
i
].
status
&
0x1
)
marked
++
;
mutex_unlock
(
&
sdev
->
lock
);
return
marked
;
}
This diff is collapsed.
Click to expand it.
drivers/platform/msm/qmp_core/qmp_sphinx_ringbuf.h
0 → 100644
View file @
13d82b1b
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 __qmp_sphinx_RINGBUF_H__
#define __qmp_sphinx_RINGBUF_H__
/*
* This header exports pingpong's API
*/
int
ringbuf_init
(
struct
qmp_sphinx_logk_dev
*
sdev
);
struct
qmp_sphinx_logk_blk
*
ringbuf_fetch_wr_block
(
struct
qmp_sphinx_logk_dev
*
sdev
);
void
ringbuf_finish_writer
(
struct
qmp_sphinx_logk_dev
*
sdev
);
void
ringbuf_cleanup
(
struct
qmp_sphinx_logk_dev
*
sdev
);
int
ringbuf_count_marked
(
struct
qmp_sphinx_logk_dev
*
sdev
);
#endif
This diff is collapsed.
Click to expand it.
include/linux/qmp_sphinx_instrumentation.h
0 → 100644
View file @
13d82b1b
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
* 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 __QMP_SPHINX_LOGK_STUB__
#define __QMP_SPHINX_LOGK_STUB__
#ifdef CONFIG_QMP_CORE
#include <linux/kernel.h>
/*(blksize(256) - hdrsize(60))*/
#define MAX_BUF_SIZE 196
extern
void
*
(
*
qmp_sphinx_logk_kernel_begin
)
(
char
**
buf
);
extern
void
(
*
qmp_sphinx_logk_kernel_end
)
(
void
*
blck
);
static
inline
void
*
qmp_sphinx_setup_buf
(
char
**
buf
)
{
void
*
blck
;
if
(
qmp_sphinx_logk_kernel_begin
&&
qmp_sphinx_logk_kernel_end
)
{
blck
=
qmp_sphinx_logk_kernel_begin
(
buf
);
if
(
!*
buf
)
{
qmp_sphinx_logk_kernel_end
(
blck
);
return
NULL
;
}
}
else
{
return
NULL
;
}
return
blck
;
}
/*
* NOTE: only sendto is going to be instrumented
* since send sys call internally calls sendto
* with 2 extra parameters
*/
static
inline
void
qmp_sphinx_logk_sendto
(
int
fd
,
void
__user
*
buff
,
size_t
len
,
unsigned
flags
,
struct
sockaddr
__user
*
addr
,
int
addr_len
)
{
char
*
buf
=
NULL
;
void
*
blck
=
NULL
;
/*sets up buf and blck correctly*/
blck
=
qmp_sphinx_setup_buf
(
&
buf
);
if
(
!
blck
)
return
;
/*fill the buf*/
snprintf
(
buf
,
MAX_BUF_SIZE
,
"-1|kernel|sendto|len=%u,fd=%d|--end"
,
(
unsigned
int
)
len
,
fd
);
end:
qmp_sphinx_logk_kernel_end
(
blck
);
}
/*
* NOTE: only recvfrom is going to be instrumented
* since recv sys call internally calls recvfrom
* with 2 extra parameters
*/
static
inline
void
qmp_sphinx_logk_recvfrom
(
int
fd
,
void
__user
*
ubuf
,
size_t
size
,
unsigned
flags
,
struct
sockaddr
__user
*
addr
,
int
__user
*
addr_len
)
{
char
*
buf
=
NULL
;
void
*
blck
=
NULL
;
/*sets up buf and blck correctly*/
blck
=
qmp_sphinx_setup_buf
(
&
buf
);
if
(
!
blck
)
return
;
/*fill the buf*/
snprintf
(
buf
,
MAX_BUF_SIZE
,
"-1|kernel|recvfrom|size=%u,fd=%d|--end"
,
(
unsigned
int
)
size
,
fd
);
end:
qmp_sphinx_logk_kernel_end
(
blck
);
}
static
inline
void
qmp_sphinx_logk_oom_adjust_write
(
pid_t
pid
,
uid_t
uid
,
int
oom_adj
)
{
char
*
buf
=
NULL
;
void
*
blck
=
NULL
;
/*sets up buf and blck correctly*/
blck
=
qmp_sphinx_setup_buf
(
&
buf
);
if
(
!
blck
)
return
;
/*fill the buf*/
snprintf
(
buf
,
MAX_BUF_SIZE
,
"-1|kernel|oom_adjust_write|app_uid=%d,app_pid=%d,oom_adj=%d|--end"
,
uid
,
pid
,
oom_adj
);
end:
qmp_sphinx_logk_kernel_end
(
blck
);
}
static
inline
void
qmp_sphinx_logk_oom_score_adj_write
(
pid_t
pid
,
uid_t
uid
,
int
oom_adj_score
)
{
char
*
buf
=
NULL
;
void
*
blck
=
NULL
;
/*sets up buf and blck correctly*/
blck
=
qmp_sphinx_setup_buf
(
&
buf
);
if
(
!
blck
)
return
;
/*fill the buf*/
snprintf
(
buf
,
MAX_BUF_SIZE
,
"-1|kernel|oom_score_adj_write|app_uid=%d,app_pid=%d,oom_adj=%d|--end"
,
uid
,
pid
,
oom_adj_score
);
end:
qmp_sphinx_logk_kernel_end
(
blck
);
}
#else
static
inline
void
qmp_sphinx_logk_sendto
(
int
fd
,
void
__user
*
buff
,
size_t
len
,
unsigned
flags
,
struct
sockaddr
__user
*
addr
,
int
addr_len
)
{
}
static
inline
void
qmp_sphinx_logk_recvfrom
(
int
fd
,
void
__user
*
ubuf
,
size_t
size
,
unsigned
flags
,
struct
sockaddr
__user
*
addr
,
int
__user
*
addr_len
)
{
}
static
inline
void
qmp_sphinx_logk_oom_adjust_write
(
pid_t
pid
,
uid_t
uid
,
int
oom_adj
)
{
}
static
inline
void
qmp_sphinx_logk_oom_score_adj_write
(
pid_t
pid
,
uid_t
uid
,
int
oom_adj_score
)
{
}
#endif
#endif
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment