Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
feb29c51
Commit
feb29c51
authored
Aug 14, 2010
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'apei' into release
parents
f2a66185
2ff729d5
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
386 additions
and
95 deletions
+386
-95
arch/x86/kernel/cpu/mcheck/mce-apei.c
arch/x86/kernel/cpu/mcheck/mce-apei.c
+2
-2
drivers/acpi/apei/Kconfig
drivers/acpi/apei/Kconfig
+9
-0
drivers/acpi/apei/Makefile
drivers/acpi/apei/Makefile
+1
-0
drivers/acpi/apei/apei-base.c
drivers/acpi/apei/apei-base.c
+2
-2
drivers/acpi/apei/erst-dbg.c
drivers/acpi/apei/erst-dbg.c
+207
-0
drivers/acpi/apei/ghes.c
drivers/acpi/apei/ghes.c
+91
-81
drivers/acpi/apei/hest.c
drivers/acpi/apei/hest.c
+70
-6
include/linux/cper.h
include/linux/cper.h
+4
-4
No files found.
arch/x86/kernel/cpu/mcheck/mce-apei.c
View file @
feb29c51
...
@@ -80,7 +80,7 @@ int apei_write_mce(struct mce *m)
...
@@ -80,7 +80,7 @@ int apei_write_mce(struct mce *m)
rcd
.
hdr
.
revision
=
CPER_RECORD_REV
;
rcd
.
hdr
.
revision
=
CPER_RECORD_REV
;
rcd
.
hdr
.
signature_end
=
CPER_SIG_END
;
rcd
.
hdr
.
signature_end
=
CPER_SIG_END
;
rcd
.
hdr
.
section_count
=
1
;
rcd
.
hdr
.
section_count
=
1
;
rcd
.
hdr
.
error_severity
=
CPER_SE
R
_FATAL
;
rcd
.
hdr
.
error_severity
=
CPER_SE
V
_FATAL
;
/* timestamp, platform_id, partition_id are all invalid */
/* timestamp, platform_id, partition_id are all invalid */
rcd
.
hdr
.
validation_bits
=
0
;
rcd
.
hdr
.
validation_bits
=
0
;
rcd
.
hdr
.
record_length
=
sizeof
(
rcd
);
rcd
.
hdr
.
record_length
=
sizeof
(
rcd
);
...
@@ -96,7 +96,7 @@ int apei_write_mce(struct mce *m)
...
@@ -96,7 +96,7 @@ int apei_write_mce(struct mce *m)
rcd
.
sec_hdr
.
validation_bits
=
0
;
rcd
.
sec_hdr
.
validation_bits
=
0
;
rcd
.
sec_hdr
.
flags
=
CPER_SEC_PRIMARY
;
rcd
.
sec_hdr
.
flags
=
CPER_SEC_PRIMARY
;
rcd
.
sec_hdr
.
section_type
=
CPER_SECTION_TYPE_MCE
;
rcd
.
sec_hdr
.
section_type
=
CPER_SECTION_TYPE_MCE
;
rcd
.
sec_hdr
.
section_severity
=
CPER_SE
R
_FATAL
;
rcd
.
sec_hdr
.
section_severity
=
CPER_SE
V
_FATAL
;
memcpy
(
&
rcd
.
mce
,
m
,
sizeof
(
*
m
));
memcpy
(
&
rcd
.
mce
,
m
,
sizeof
(
*
m
));
...
...
drivers/acpi/apei/Kconfig
View file @
feb29c51
...
@@ -28,3 +28,12 @@ config ACPI_APEI_EINJ
...
@@ -28,3 +28,12 @@ config ACPI_APEI_EINJ
EINJ provides a hardware error injection mechanism, it is
EINJ provides a hardware error injection mechanism, it is
mainly used for debugging and testing the other parts of
mainly used for debugging and testing the other parts of
APEI and some other RAS features.
APEI and some other RAS features.
config ACPI_APEI_ERST_DEBUG
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
depends on ACPI_APEI
help
ERST is a way provided by APEI to save and retrieve hardware
error infomation to and from a persistent store. Enable this
if you want to debugging and testing the ERST kernel support
and firmware implementation.
drivers/acpi/apei/Makefile
View file @
feb29c51
obj-$(CONFIG_ACPI_APEI)
+=
apei.o
obj-$(CONFIG_ACPI_APEI)
+=
apei.o
obj-$(CONFIG_ACPI_APEI_GHES)
+=
ghes.o
obj-$(CONFIG_ACPI_APEI_GHES)
+=
ghes.o
obj-$(CONFIG_ACPI_APEI_EINJ)
+=
einj.o
obj-$(CONFIG_ACPI_APEI_EINJ)
+=
einj.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG)
+=
erst-dbg.o
apei-y
:=
apei-base.o hest.o cper.o erst.o
apei-y
:=
apei-base.o hest.o cper.o erst.o
drivers/acpi/apei/apei-base.c
View file @
feb29c51
...
@@ -482,14 +482,14 @@ int apei_resources_request(struct apei_resources *resources,
...
@@ -482,14 +482,14 @@ int apei_resources_request(struct apei_resources *resources,
list_for_each_entry
(
res
,
&
resources
->
ioport
,
list
)
{
list_for_each_entry
(
res
,
&
resources
->
ioport
,
list
)
{
if
(
res
==
res_bak
)
if
(
res
==
res_bak
)
break
;
break
;
release_
mem_
region
(
res
->
start
,
res
->
end
-
res
->
start
);
release_region
(
res
->
start
,
res
->
end
-
res
->
start
);
}
}
res_bak
=
NULL
;
res_bak
=
NULL
;
err_unmap_iomem:
err_unmap_iomem:
list_for_each_entry
(
res
,
&
resources
->
iomem
,
list
)
{
list_for_each_entry
(
res
,
&
resources
->
iomem
,
list
)
{
if
(
res
==
res_bak
)
if
(
res
==
res_bak
)
break
;
break
;
release_region
(
res
->
start
,
res
->
end
-
res
->
start
);
release_
mem_
region
(
res
->
start
,
res
->
end
-
res
->
start
);
}
}
return
-
EINVAL
;
return
-
EINVAL
;
}
}
...
...
drivers/acpi/apei/erst-dbg.c
0 → 100644
View file @
feb29c51
/*
* APEI Error Record Serialization Table debug support
*
* ERST is a way provided by APEI to save and retrieve hardware error
* infomation to and from a persistent store. This file provide the
* debugging/testing support for ERST kernel support and firmware
* implementation.
*
* Copyright 2010 Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <acpi/apei.h>
#include <linux/miscdevice.h>
#include "apei-internal.h"
#define ERST_DBG_PFX "ERST DBG: "
#define ERST_DBG_RECORD_LEN_MAX 4096
static
void
*
erst_dbg_buf
;
static
unsigned
int
erst_dbg_buf_len
;
/* Prevent erst_dbg_read/write from being invoked concurrently */
static
DEFINE_MUTEX
(
erst_dbg_mutex
);
static
int
erst_dbg_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
erst_disable
)
return
-
ENODEV
;
return
nonseekable_open
(
inode
,
file
);
}
static
long
erst_dbg_ioctl
(
struct
file
*
f
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
rc
;
u64
record_id
;
u32
record_count
;
switch
(
cmd
)
{
case
APEI_ERST_CLEAR_RECORD
:
rc
=
copy_from_user
(
&
record_id
,
(
void
__user
*
)
arg
,
sizeof
(
record_id
));
if
(
rc
)
return
-
EFAULT
;
return
erst_clear
(
record_id
);
case
APEI_ERST_GET_RECORD_COUNT
:
rc
=
erst_get_record_count
();
if
(
rc
<
0
)
return
rc
;
record_count
=
rc
;
rc
=
put_user
(
record_count
,
(
u32
__user
*
)
arg
);
if
(
rc
)
return
rc
;
return
0
;
default:
return
-
ENOTTY
;
}
}
static
ssize_t
erst_dbg_read
(
struct
file
*
filp
,
char
__user
*
ubuf
,
size_t
usize
,
loff_t
*
off
)
{
int
rc
;
ssize_t
len
=
0
;
u64
id
;
if
(
*
off
!=
0
)
return
-
EINVAL
;
if
(
mutex_lock_interruptible
(
&
erst_dbg_mutex
)
!=
0
)
return
-
EINTR
;
retry_next:
rc
=
erst_get_next_record_id
(
&
id
);
if
(
rc
)
goto
out
;
/* no more record */
if
(
id
==
APEI_ERST_INVALID_RECORD_ID
)
goto
out
;
retry:
rc
=
len
=
erst_read
(
id
,
erst_dbg_buf
,
erst_dbg_buf_len
);
/* The record may be cleared by others, try read next record */
if
(
rc
==
-
ENOENT
)
goto
retry_next
;
if
(
rc
<
0
)
goto
out
;
if
(
len
>
ERST_DBG_RECORD_LEN_MAX
)
{
pr_warning
(
ERST_DBG_PFX
"Record (ID: 0x%llx) length is too long: %zd
\n
"
,
id
,
len
);
rc
=
-
EIO
;
goto
out
;
}
if
(
len
>
erst_dbg_buf_len
)
{
kfree
(
erst_dbg_buf
);
rc
=
-
ENOMEM
;
erst_dbg_buf
=
kmalloc
(
len
,
GFP_KERNEL
);
if
(
!
erst_dbg_buf
)
goto
out
;
erst_dbg_buf_len
=
len
;
goto
retry
;
}
rc
=
-
EINVAL
;
if
(
len
>
usize
)
goto
out
;
rc
=
-
EFAULT
;
if
(
copy_to_user
(
ubuf
,
erst_dbg_buf
,
len
))
goto
out
;
rc
=
0
;
out:
mutex_unlock
(
&
erst_dbg_mutex
);
return
rc
?
rc
:
len
;
}
static
ssize_t
erst_dbg_write
(
struct
file
*
filp
,
const
char
__user
*
ubuf
,
size_t
usize
,
loff_t
*
off
)
{
int
rc
;
struct
cper_record_header
*
rcd
;
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EPERM
;
if
(
usize
>
ERST_DBG_RECORD_LEN_MAX
)
{
pr_err
(
ERST_DBG_PFX
"Too long record to be written
\n
"
);
return
-
EINVAL
;
}
if
(
mutex_lock_interruptible
(
&
erst_dbg_mutex
))
return
-
EINTR
;
if
(
usize
>
erst_dbg_buf_len
)
{
kfree
(
erst_dbg_buf
);
rc
=
-
ENOMEM
;
erst_dbg_buf
=
kmalloc
(
usize
,
GFP_KERNEL
);
if
(
!
erst_dbg_buf
)
goto
out
;
erst_dbg_buf_len
=
usize
;
}
rc
=
copy_from_user
(
erst_dbg_buf
,
ubuf
,
usize
);
if
(
rc
)
{
rc
=
-
EFAULT
;
goto
out
;
}
rcd
=
erst_dbg_buf
;
rc
=
-
EINVAL
;
if
(
rcd
->
record_length
!=
usize
)
goto
out
;
rc
=
erst_write
(
erst_dbg_buf
);
out:
mutex_unlock
(
&
erst_dbg_mutex
);
return
rc
<
0
?
rc
:
usize
;
}
static
const
struct
file_operations
erst_dbg_ops
=
{
.
owner
=
THIS_MODULE
,
.
open
=
erst_dbg_open
,
.
read
=
erst_dbg_read
,
.
write
=
erst_dbg_write
,
.
unlocked_ioctl
=
erst_dbg_ioctl
,
};
static
struct
miscdevice
erst_dbg_dev
=
{
.
minor
=
MISC_DYNAMIC_MINOR
,
.
name
=
"erst_dbg"
,
.
fops
=
&
erst_dbg_ops
,
};
static
__init
int
erst_dbg_init
(
void
)
{
return
misc_register
(
&
erst_dbg_dev
);
}
static
__exit
void
erst_dbg_exit
(
void
)
{
misc_deregister
(
&
erst_dbg_dev
);
kfree
(
erst_dbg_buf
);
}
module_init
(
erst_dbg_init
);
module_exit
(
erst_dbg_exit
);
MODULE_AUTHOR
(
"Huang Ying"
);
MODULE_DESCRIPTION
(
"APEI Error Record Serialization Table debug support"
);
MODULE_LICENSE
(
"GPL"
);
drivers/acpi/apei/ghes.c
View file @
feb29c51
...
@@ -41,6 +41,8 @@
...
@@ -41,6 +41,8 @@
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/cper.h>
#include <linux/cper.h>
#include <linux/kdebug.h>
#include <linux/kdebug.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <acpi/apei.h>
#include <acpi/apei.h>
#include <acpi/atomicio.h>
#include <acpi/atomicio.h>
#include <acpi/hed.h>
#include <acpi/hed.h>
...
@@ -87,6 +89,7 @@ struct ghes {
...
@@ -87,6 +89,7 @@ struct ghes {
* used for that.
* used for that.
*/
*/
static
LIST_HEAD
(
ghes_sci
);
static
LIST_HEAD
(
ghes_sci
);
static
DEFINE_MUTEX
(
ghes_list_mutex
);
static
struct
ghes
*
ghes_new
(
struct
acpi_hest_generic
*
generic
)
static
struct
ghes
*
ghes_new
(
struct
acpi_hest_generic
*
generic
)
{
{
...
@@ -132,26 +135,26 @@ static void ghes_fini(struct ghes *ghes)
...
@@ -132,26 +135,26 @@ static void ghes_fini(struct ghes *ghes)
}
}
enum
{
enum
{
GHES_SE
R
_NO
=
0x0
,
GHES_SE
V
_NO
=
0x0
,
GHES_SE
R
_CORRECTED
=
0x1
,
GHES_SE
V
_CORRECTED
=
0x1
,
GHES_SE
R
_RECOVERABLE
=
0x2
,
GHES_SE
V
_RECOVERABLE
=
0x2
,
GHES_SE
R
_PANIC
=
0x3
,
GHES_SE
V
_PANIC
=
0x3
,
};
};
static
inline
int
ghes_severity
(
int
severity
)
static
inline
int
ghes_severity
(
int
severity
)
{
{
switch
(
severity
)
{
switch
(
severity
)
{
case
CPER_SE
R
_INFORMATIONAL
:
case
CPER_SE
V
_INFORMATIONAL
:
return
GHES_SE
R
_NO
;
return
GHES_SE
V
_NO
;
case
CPER_SE
R
_CORRECTED
:
case
CPER_SE
V
_CORRECTED
:
return
GHES_SE
R
_CORRECTED
;
return
GHES_SE
V
_CORRECTED
;
case
CPER_SE
R
_RECOVERABLE
:
case
CPER_SE
V
_RECOVERABLE
:
return
GHES_SE
R
_RECOVERABLE
;
return
GHES_SE
V
_RECOVERABLE
;
case
CPER_SE
R
_FATAL
:
case
CPER_SE
V
_FATAL
:
return
GHES_SE
R
_PANIC
;
return
GHES_SE
V
_PANIC
;
default:
default:
/* Unkown, go panic */
/* Unkown, go panic */
return
GHES_SE
R
_PANIC
;
return
GHES_SE
V
_PANIC
;
}
}
}
}
...
@@ -237,16 +240,16 @@ static void ghes_clear_estatus(struct ghes *ghes)
...
@@ -237,16 +240,16 @@ static void ghes_clear_estatus(struct ghes *ghes)
static
void
ghes_do_proc
(
struct
ghes
*
ghes
)
static
void
ghes_do_proc
(
struct
ghes
*
ghes
)
{
{
int
se
r
,
processed
=
0
;
int
se
v
,
processed
=
0
;
struct
acpi_hest_generic_data
*
gdata
;
struct
acpi_hest_generic_data
*
gdata
;
se
r
=
ghes_severity
(
ghes
->
estatus
->
error_severity
);
se
v
=
ghes_severity
(
ghes
->
estatus
->
error_severity
);
apei_estatus_for_each_section
(
ghes
->
estatus
,
gdata
)
{
apei_estatus_for_each_section
(
ghes
->
estatus
,
gdata
)
{
#ifdef CONFIG_X86_MCE
#ifdef CONFIG_X86_MCE
if
(
!
uuid_le_cmp
(
*
(
uuid_le
*
)
gdata
->
section_type
,
if
(
!
uuid_le_cmp
(
*
(
uuid_le
*
)
gdata
->
section_type
,
CPER_SEC_PLATFORM_MEM
))
{
CPER_SEC_PLATFORM_MEM
))
{
apei_mce_report_mem_error
(
apei_mce_report_mem_error
(
se
r
==
GHES_SER
_CORRECTED
,
se
v
==
GHES_SEV
_CORRECTED
,
(
struct
cper_sec_mem_err
*
)(
gdata
+
1
));
(
struct
cper_sec_mem_err
*
)(
gdata
+
1
));
processed
=
1
;
processed
=
1
;
}
}
...
@@ -293,18 +296,15 @@ static struct notifier_block ghes_notifier_sci = {
...
@@ -293,18 +296,15 @@ static struct notifier_block ghes_notifier_sci = {
.
notifier_call
=
ghes_notify_sci
,
.
notifier_call
=
ghes_notify_sci
,
};
};
static
int
hest_ghes_parse
(
struct
acpi_hest_header
*
hest_hdr
,
void
*
data
)
static
int
__devinit
ghes_probe
(
struct
platform_device
*
ghes_dev
)
{
{
struct
acpi_hest_generic
*
generic
;
struct
acpi_hest_generic
*
generic
;
struct
ghes
*
ghes
=
NULL
;
struct
ghes
*
ghes
=
NULL
;
int
rc
=
0
;
int
rc
=
-
EINVAL
;
if
(
hest_hdr
->
type
!=
ACPI_HEST_TYPE_GENERIC_ERROR
)
generic
=
ghes_dev
->
dev
.
platform_data
;
return
0
;
generic
=
(
struct
acpi_hest_generic
*
)
hest_hdr
;
if
(
!
generic
->
enabled
)
if
(
!
generic
->
enabled
)
return
0
;
return
-
ENODEV
;
if
(
generic
->
error_block_length
<
if
(
generic
->
error_block_length
<
sizeof
(
struct
acpi_hest_generic_status
))
{
sizeof
(
struct
acpi_hest_generic_status
))
{
...
@@ -327,62 +327,91 @@ static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
...
@@ -327,62 +327,91 @@ static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
ghes
=
NULL
;
ghes
=
NULL
;
goto
err
;
goto
err
;
}
}
switch
(
generic
->
notify
.
type
)
{
if
(
generic
->
notify
.
type
==
ACPI_HEST_NOTIFY_SCI
)
{
case
ACPI_HEST_NOTIFY_POLLED
:
mutex_lock
(
&
ghes_list_mutex
);
pr_warning
(
GHES_PFX
"Generic hardware error source: %d notified via POLL is not supported!
\n
"
,
generic
->
header
.
source_id
);
break
;
case
ACPI_HEST_NOTIFY_EXTERNAL
:
case
ACPI_HEST_NOTIFY_LOCAL
:
pr_warning
(
GHES_PFX
"Generic hardware error source: %d notified via IRQ is not supported!
\n
"
,
generic
->
header
.
source_id
);
break
;
case
ACPI_HEST_NOTIFY_SCI
:
if
(
list_empty
(
&
ghes_sci
))
if
(
list_empty
(
&
ghes_sci
))
register_acpi_hed_notifier
(
&
ghes_notifier_sci
);
register_acpi_hed_notifier
(
&
ghes_notifier_sci
);
list_add_rcu
(
&
ghes
->
list
,
&
ghes_sci
);
list_add_rcu
(
&
ghes
->
list
,
&
ghes_sci
);
break
;
mutex_unlock
(
&
ghes_list_mutex
);
case
ACPI_HEST_NOTIFY_NMI
:
}
else
{
pr_warning
(
GHES_PFX
unsigned
char
*
notify
=
NULL
;
"Generic hardware error source: %d notified via NMI is not supported!
\n
"
,
generic
->
header
.
source_id
);
switch
(
generic
->
notify
.
type
)
{
break
;
case
ACPI_HEST_NOTIFY_POLLED
:
default:
notify
=
"POLL"
;
pr_warning
(
FW_WARN
GHES_PFX
break
;
"Unknown notification type: %u for generic hardware error source: %d
\n
"
,
case
ACPI_HEST_NOTIFY_EXTERNAL
:
generic
->
notify
.
type
,
generic
->
header
.
source_id
);
case
ACPI_HEST_NOTIFY_LOCAL
:
break
;
notify
=
"IRQ"
;
break
;
case
ACPI_HEST_NOTIFY_NMI
:
notify
=
"NMI"
;
break
;
}
if
(
notify
)
{
pr_warning
(
GHES_PFX
"Generic hardware error source: %d notified via %s is not supported!
\n
"
,
generic
->
header
.
source_id
,
notify
);
}
else
{
pr_warning
(
FW_WARN
GHES_PFX
"Unknown notification type: %u for generic hardware error source: %d
\n
"
,
generic
->
notify
.
type
,
generic
->
header
.
source_id
);
}
rc
=
-
ENODEV
;
goto
err
;
}
}
platform_set_drvdata
(
ghes_dev
,
ghes
);
return
0
;
return
0
;
err:
err:
if
(
ghes
)
if
(
ghes
)
{
ghes_fini
(
ghes
);
ghes_fini
(
ghes
);
kfree
(
ghes
);
}
return
rc
;
return
rc
;
}
}
static
void
ghes_cleanup
(
void
)
static
int
__devexit
ghes_remove
(
struct
platform_device
*
ghes_dev
)
{
{
struct
ghes
*
ghes
,
*
nghes
;
struct
ghes
*
ghes
;
struct
acpi_hest_generic
*
generic
;
if
(
!
list_empty
(
&
ghes_sci
))
ghes
=
platform_get_drvdata
(
ghes_dev
);
unregister_acpi_hed_notifier
(
&
ghes_notifier_sci
);
generic
=
ghes
->
generic
;
switch
(
generic
->
notify
.
type
)
{
case
ACPI_HEST_NOTIFY_SCI
:
mutex_lock
(
&
ghes_list_mutex
);
list_del_rcu
(
&
ghes
->
list
);
if
(
list_empty
(
&
ghes_sci
))
unregister_acpi_hed_notifier
(
&
ghes_notifier_sci
);
mutex_unlock
(
&
ghes_list_mutex
);
break
;
default:
BUG
();
break
;
}
synchronize_rcu
();
synchronize_rcu
();
ghes_fini
(
ghes
);
kfree
(
ghes
);
list_for_each_entry_safe
(
ghes
,
nghes
,
&
ghes_sci
,
list
)
{
platform_set_drvdata
(
ghes_dev
,
NULL
);
list_del
(
&
ghes
->
list
);
ghes_fini
(
ghes
);
return
0
;
kfree
(
ghes
);
}
}
}
static
struct
platform_driver
ghes_platform_driver
=
{
.
driver
=
{
.
name
=
"GHES"
,
.
owner
=
THIS_MODULE
,
},
.
probe
=
ghes_probe
,
.
remove
=
ghes_remove
,
};
static
int
__init
ghes_init
(
void
)
static
int
__init
ghes_init
(
void
)
{
{
int
rc
;
if
(
acpi_disabled
)
if
(
acpi_disabled
)
return
-
ENODEV
;
return
-
ENODEV
;
...
@@ -391,32 +420,12 @@ static int __init ghes_init(void)
...
@@ -391,32 +420,12 @@ static int __init ghes_init(void)
return
-
EINVAL
;
return
-
EINVAL
;
}
}
rc
=
apei_hest_parse
(
hest_ghes_parse
,
NULL
);
return
platform_driver_register
(
&
ghes_platform_driver
);
if
(
rc
)
{
pr_err
(
GHES_PFX
"Error during parsing HEST generic hardware error sources.
\n
"
);
goto
err_cleanup
;
}
if
(
list_empty
(
&
ghes_sci
))
{
pr_info
(
GHES_PFX
"No functional generic hardware error sources.
\n
"
);
rc
=
-
ENODEV
;
goto
err_cleanup
;
}
pr_info
(
GHES_PFX
"Generic Hardware Error Source support is initialized.
\n
"
);
return
0
;
err_cleanup:
ghes_cleanup
();
return
rc
;
}
}
static
void
__exit
ghes_exit
(
void
)
static
void
__exit
ghes_exit
(
void
)
{
{
ghes_cleanup
(
);
platform_driver_unregister
(
&
ghes_platform_driver
);
}
}
module_init
(
ghes_init
);
module_init
(
ghes_init
);
...
@@ -425,3 +434,4 @@ module_exit(ghes_exit);
...
@@ -425,3 +434,4 @@ module_exit(ghes_exit);
MODULE_AUTHOR
(
"Huang Ying"
);
MODULE_AUTHOR
(
"Huang Ying"
);
MODULE_DESCRIPTION
(
"APEI Generic Hardware Error Source support"
);
MODULE_DESCRIPTION
(
"APEI Generic Hardware Error Source support"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_ALIAS
(
"platform:GHES"
);
drivers/acpi/apei/hest.c
View file @
feb29c51
...
@@ -34,6 +34,7 @@
...
@@ -34,6 +34,7 @@
#include <linux/kdebug.h>
#include <linux/kdebug.h>
#include <linux/highmem.h>
#include <linux/highmem.h>
#include <linux/io.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <acpi/apei.h>
#include <acpi/apei.h>
#include "apei-internal.h"
#include "apei-internal.h"
...
@@ -47,11 +48,6 @@ EXPORT_SYMBOL_GPL(hest_disable);
...
@@ -47,11 +48,6 @@ EXPORT_SYMBOL_GPL(hest_disable);
static
struct
acpi_table_hest
*
hest_tab
;
static
struct
acpi_table_hest
*
hest_tab
;
static
int
hest_void_parse
(
struct
acpi_hest_header
*
hest_hdr
,
void
*
data
)
{
return
0
;
}
static
int
hest_esrc_len_tab
[
ACPI_HEST_TYPE_RESERVED
]
=
{
static
int
hest_esrc_len_tab
[
ACPI_HEST_TYPE_RESERVED
]
=
{
[
ACPI_HEST_TYPE_IA32_CHECK
]
=
-
1
,
/* need further calculation */
[
ACPI_HEST_TYPE_IA32_CHECK
]
=
-
1
,
/* need further calculation */
[
ACPI_HEST_TYPE_IA32_CORRECTED_CHECK
]
=
-
1
,
[
ACPI_HEST_TYPE_IA32_CORRECTED_CHECK
]
=
-
1
,
...
@@ -125,6 +121,69 @@ int apei_hest_parse(apei_hest_func_t func, void *data)
...
@@ -125,6 +121,69 @@ int apei_hest_parse(apei_hest_func_t func, void *data)
}
}
EXPORT_SYMBOL_GPL
(
apei_hest_parse
);
EXPORT_SYMBOL_GPL
(
apei_hest_parse
);
struct
ghes_arr
{
struct
platform_device
**
ghes_devs
;
unsigned
int
count
;
};
static
int
hest_parse_ghes_count
(
struct
acpi_hest_header
*
hest_hdr
,
void
*
data
)
{
int
*
count
=
data
;
if
(
hest_hdr
->
type
==
ACPI_HEST_TYPE_GENERIC_ERROR
)
(
*
count
)
++
;
return
0
;
}
static
int
hest_parse_ghes
(
struct
acpi_hest_header
*
hest_hdr
,
void
*
data
)
{
struct
acpi_hest_generic
*
generic
;
struct
platform_device
*
ghes_dev
;
struct
ghes_arr
*
ghes_arr
=
data
;
int
rc
;
if
(
hest_hdr
->
type
!=
ACPI_HEST_TYPE_GENERIC_ERROR
)
return
0
;
generic
=
(
struct
acpi_hest_generic
*
)
hest_hdr
;
if
(
!
generic
->
enabled
)
return
0
;
ghes_dev
=
platform_device_alloc
(
"GHES"
,
hest_hdr
->
source_id
);
if
(
!
ghes_dev
)
return
-
ENOMEM
;
ghes_dev
->
dev
.
platform_data
=
generic
;
rc
=
platform_device_add
(
ghes_dev
);
if
(
rc
)
goto
err
;
ghes_arr
->
ghes_devs
[
ghes_arr
->
count
++
]
=
ghes_dev
;
return
0
;
err:
platform_device_put
(
ghes_dev
);
return
rc
;
}
static
int
hest_ghes_dev_register
(
unsigned
int
ghes_count
)
{
int
rc
,
i
;
struct
ghes_arr
ghes_arr
;
ghes_arr
.
count
=
0
;
ghes_arr
.
ghes_devs
=
kmalloc
(
sizeof
(
void
*
)
*
ghes_count
,
GFP_KERNEL
);
if
(
!
ghes_arr
.
ghes_devs
)
return
-
ENOMEM
;
rc
=
apei_hest_parse
(
hest_parse_ghes
,
&
ghes_arr
);
if
(
rc
)
goto
err
;
out:
kfree
(
ghes_arr
.
ghes_devs
);
return
rc
;
err:
for
(
i
=
0
;
i
<
ghes_arr
.
count
;
i
++
)
platform_device_unregister
(
ghes_arr
.
ghes_devs
[
i
]);
goto
out
;
}
static
int
__init
setup_hest_disable
(
char
*
str
)
static
int
__init
setup_hest_disable
(
char
*
str
)
{
{
hest_disable
=
1
;
hest_disable
=
1
;
...
@@ -137,6 +196,7 @@ static int __init hest_init(void)
...
@@ -137,6 +196,7 @@ static int __init hest_init(void)
{
{
acpi_status
status
;
acpi_status
status
;
int
rc
=
-
ENODEV
;
int
rc
=
-
ENODEV
;
unsigned
int
ghes_count
=
0
;
if
(
acpi_disabled
)
if
(
acpi_disabled
)
goto
err
;
goto
err
;
...
@@ -158,7 +218,11 @@ static int __init hest_init(void)
...
@@ -158,7 +218,11 @@ static int __init hest_init(void)
goto
err
;
goto
err
;
}
}
rc
=
apei_hest_parse
(
hest_void_parse
,
NULL
);
rc
=
apei_hest_parse
(
hest_parse_ghes_count
,
&
ghes_count
);
if
(
rc
)
goto
err
;
rc
=
hest_ghes_dev_register
(
ghes_count
);
if
(
rc
)
if
(
rc
)
goto
err
;
goto
err
;
...
...
include/linux/cper.h
View file @
feb29c51
...
@@ -39,10 +39,10 @@
...
@@ -39,10 +39,10 @@
* Severity difinition for error_severity in struct cper_record_header
* Severity difinition for error_severity in struct cper_record_header
* and section_severity in struct cper_section_descriptor
* and section_severity in struct cper_section_descriptor
*/
*/
#define CPER_SE
R
_RECOVERABLE 0x0
#define CPER_SE
V
_RECOVERABLE 0x0
#define CPER_SE
R
_FATAL 0x1
#define CPER_SE
V
_FATAL 0x1
#define CPER_SE
R
_CORRECTED 0x2
#define CPER_SE
V
_CORRECTED 0x2
#define CPER_SE
R
_INFORMATIONAL 0x3
#define CPER_SE
V
_INFORMATIONAL 0x3
/*
/*
* Validation bits difinition for validation_bits in struct
* Validation bits difinition for validation_bits in struct
...
...
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