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
nexedi
linux
Commits
d2918253
Commit
d2918253
authored
Apr 30, 2007
by
Tony Luck
Browse files
Options
Browse Files
Download
Plain Diff
Pull mem-attribute into release branch
parents
b643b0fd
ddd83eff
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
392 additions
and
58 deletions
+392
-58
Documentation/ia64/aliasing-test.c
Documentation/ia64/aliasing-test.c
+247
-0
Documentation/ia64/aliasing.txt
Documentation/ia64/aliasing.txt
+37
-34
arch/ia64/kernel/efi.c
arch/ia64/kernel/efi.c
+43
-3
arch/ia64/mm/ioremap.c
arch/ia64/mm/ioremap.c
+64
-14
arch/ia64/pci/pci.c
arch/ia64/pci/pci.c
+0
-2
include/asm-ia64/io.h
include/asm-ia64/io.h
+1
-5
No files found.
Documentation/ia64/aliasing-test.c
0 → 100644
View file @
d2918253
/*
* Exercise /dev/mem mmap cases that have been troublesome in the past
*
* (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
* Bjorn Helgaas <bjorn.helgaas@hp.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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
int
sum
;
int
map_mem
(
char
*
path
,
off_t
offset
,
size_t
length
,
int
touch
)
{
int
fd
,
rc
;
void
*
addr
;
int
*
c
;
fd
=
open
(
path
,
O_RDWR
);
if
(
fd
==
-
1
)
{
perror
(
path
);
return
-
1
;
}
addr
=
mmap
(
NULL
,
length
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
offset
);
if
(
addr
==
MAP_FAILED
)
return
1
;
if
(
touch
)
{
c
=
(
int
*
)
addr
;
while
(
c
<
(
int
*
)
(
offset
+
length
))
sum
+=
*
c
++
;
}
rc
=
munmap
(
addr
,
length
);
if
(
rc
==
-
1
)
{
perror
(
"munmap"
);
return
-
1
;
}
close
(
fd
);
return
0
;
}
int
scan_sysfs
(
char
*
path
,
char
*
file
,
off_t
offset
,
size_t
length
,
int
touch
)
{
struct
dirent
**
namelist
;
char
*
name
,
*
path2
;
int
i
,
n
,
r
,
rc
,
result
=
0
;
struct
stat
buf
;
n
=
scandir
(
path
,
&
namelist
,
0
,
alphasort
);
if
(
n
<
0
)
{
perror
(
"scandir"
);
return
-
1
;
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
name
=
namelist
[
i
]
->
d_name
;
if
(
fnmatch
(
"."
,
name
,
0
)
==
0
)
goto
skip
;
if
(
fnmatch
(
".."
,
name
,
0
)
==
0
)
goto
skip
;
path2
=
malloc
(
strlen
(
path
)
+
strlen
(
name
)
+
3
);
strcpy
(
path2
,
path
);
strcat
(
path2
,
"/"
);
strcat
(
path2
,
name
);
if
(
fnmatch
(
file
,
name
,
0
)
==
0
)
{
rc
=
map_mem
(
path2
,
offset
,
length
,
touch
);
if
(
rc
==
0
)
fprintf
(
stderr
,
"PASS: %s 0x%lx-0x%lx is %s
\n
"
,
path2
,
offset
,
offset
+
length
,
touch
?
"readable"
:
"mappable"
);
else
if
(
rc
>
0
)
fprintf
(
stderr
,
"PASS: %s 0x%lx-0x%lx not mappable
\n
"
,
path2
,
offset
,
offset
+
length
);
else
{
fprintf
(
stderr
,
"FAIL: %s 0x%lx-0x%lx not accessible
\n
"
,
path2
,
offset
,
offset
+
length
);
return
rc
;
}
}
else
{
r
=
lstat
(
path2
,
&
buf
);
if
(
r
==
0
&&
S_ISDIR
(
buf
.
st_mode
))
{
rc
=
scan_sysfs
(
path2
,
file
,
offset
,
length
,
touch
);
if
(
rc
<
0
)
return
rc
;
}
}
result
|=
rc
;
free
(
path2
);
skip:
free
(
namelist
[
i
]);
}
free
(
namelist
);
return
rc
;
}
char
buf
[
1024
];
int
read_rom
(
char
*
path
)
{
int
fd
,
rc
;
size_t
size
=
0
;
fd
=
open
(
path
,
O_RDWR
);
if
(
fd
==
-
1
)
{
perror
(
path
);
return
-
1
;
}
rc
=
write
(
fd
,
"1"
,
2
);
if
(
rc
<=
0
)
{
perror
(
"write"
);
return
-
1
;
}
do
{
rc
=
read
(
fd
,
buf
,
sizeof
(
buf
));
if
(
rc
>
0
)
size
+=
rc
;
}
while
(
rc
>
0
);
close
(
fd
);
return
size
;
}
int
scan_rom
(
char
*
path
,
char
*
file
)
{
struct
dirent
**
namelist
;
char
*
name
,
*
path2
;
int
i
,
n
,
r
,
rc
,
result
=
0
;
struct
stat
buf
;
n
=
scandir
(
path
,
&
namelist
,
0
,
alphasort
);
if
(
n
<
0
)
{
perror
(
"scandir"
);
return
-
1
;
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
name
=
namelist
[
i
]
->
d_name
;
if
(
fnmatch
(
"."
,
name
,
0
)
==
0
)
goto
skip
;
if
(
fnmatch
(
".."
,
name
,
0
)
==
0
)
goto
skip
;
path2
=
malloc
(
strlen
(
path
)
+
strlen
(
name
)
+
3
);
strcpy
(
path2
,
path
);
strcat
(
path2
,
"/"
);
strcat
(
path2
,
name
);
if
(
fnmatch
(
file
,
name
,
0
)
==
0
)
{
rc
=
read_rom
(
path2
);
/*
* It's OK if the ROM is unreadable. Maybe there
* is no ROM, or some other error ocurred. The
* important thing is that no MCA happened.
*/
if
(
rc
>
0
)
fprintf
(
stderr
,
"PASS: %s read %ld bytes
\n
"
,
path2
,
rc
);
else
{
fprintf
(
stderr
,
"PASS: %s not readable
\n
"
,
path2
);
return
rc
;
}
}
else
{
r
=
lstat
(
path2
,
&
buf
);
if
(
r
==
0
&&
S_ISDIR
(
buf
.
st_mode
))
{
rc
=
scan_rom
(
path2
,
file
);
if
(
rc
<
0
)
return
rc
;
}
}
result
|=
rc
;
free
(
path2
);
skip:
free
(
namelist
[
i
]);
}
free
(
namelist
);
return
rc
;
}
main
()
{
int
rc
;
if
(
map_mem
(
"/dev/mem"
,
0
,
0xA0000
,
1
)
==
0
)
fprintf
(
stderr
,
"PASS: /dev/mem 0x0-0xa0000 is readable
\n
"
);
else
fprintf
(
stderr
,
"FAIL: /dev/mem 0x0-0xa0000 not accessible
\n
"
);
/*
* It's not safe to blindly read the VGA frame buffer. If you know
* how to poke the card the right way, it should respond, but it's
* not safe in general. Many machines, e.g., Intel chipsets, cover
* up a non-responding card by just returning -1, but others will
* report the failure as a machine check.
*/
if
(
map_mem
(
"/dev/mem"
,
0xA0000
,
0x20000
,
0
)
==
0
)
fprintf
(
stderr
,
"PASS: /dev/mem 0xa0000-0xc0000 is mappable
\n
"
);
else
fprintf
(
stderr
,
"FAIL: /dev/mem 0xa0000-0xc0000 not accessible
\n
"
);
if
(
map_mem
(
"/dev/mem"
,
0xC0000
,
0x40000
,
1
)
==
0
)
fprintf
(
stderr
,
"PASS: /dev/mem 0xc0000-0x100000 is readable
\n
"
);
else
fprintf
(
stderr
,
"FAIL: /dev/mem 0xc0000-0x100000 not accessible
\n
"
);
/*
* Often you can map all the individual pieces above (0-0xA0000,
* 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
* thing at once. This is because the individual pieces use different
* attributes, and there's no single attribute supported over the
* whole region.
*/
rc
=
map_mem
(
"/dev/mem"
,
0
,
1024
*
1024
,
0
);
if
(
rc
==
0
)
fprintf
(
stderr
,
"PASS: /dev/mem 0x0-0x100000 is mappable
\n
"
);
else
if
(
rc
>
0
)
fprintf
(
stderr
,
"PASS: /dev/mem 0x0-0x100000 not mappable
\n
"
);
else
fprintf
(
stderr
,
"FAIL: /dev/mem 0x0-0x100000 not accessible
\n
"
);
scan_sysfs
(
"/sys/class/pci_bus"
,
"legacy_mem"
,
0
,
0xA0000
,
1
);
scan_sysfs
(
"/sys/class/pci_bus"
,
"legacy_mem"
,
0xA0000
,
0x20000
,
0
);
scan_sysfs
(
"/sys/class/pci_bus"
,
"legacy_mem"
,
0xC0000
,
0x40000
,
1
);
scan_sysfs
(
"/sys/class/pci_bus"
,
"legacy_mem"
,
0
,
1024
*
1024
,
0
);
scan_rom
(
"/sys/devices"
,
"rom"
);
}
Documentation/ia64/aliasing.txt
View file @
d2918253
...
...
@@ -112,16 +112,6 @@ POTENTIAL ATTRIBUTE ALIASING CASES
The /dev/mem mmap constraints apply.
However, since this is for mapping legacy MMIO space, WB access
does not make sense. This matters on machines without legacy
VGA support: these machines may have WB memory for the entire
first megabyte (or even the entire first granule).
On these machines, we could mmap legacy_mem as WB, which would
be safe in terms of attribute aliasing, but X has no way of
knowing that it is accessing regular memory, not a frame buffer,
so the kernel should fail the mmap rather than doing it with WB.
read/write of /dev/mem
This uses copy_from_user(), which implicitly uses a kernel
...
...
@@ -138,14 +128,20 @@ POTENTIAL ATTRIBUTE ALIASING CASES
ioremap()
This returns a kernel identity mapping for use inside the
kernel.
This returns a mapping for use inside the kernel.
If the region is in kern_memmap, we should use the attribute
specified there. Otherwise, if the EFI memory map reports that
the entire granule supports WB, we should use that (granules
that are partially reserved or occupied by firmware do not appear
in kern_memmap). Otherwise, we should use a UC mapping.
specified there.
If the EFI memory map reports that the entire granule supports
WB, we should use that (granules that are partially reserved
or occupied by firmware do not appear in kern_memmap).
If the granule contains non-WB memory, but we can cover the
region safely with kernel page table mappings, we can use
ioremap_page_range() as most other architectures do.
Failing all of the above, we have to fall back to a UC mapping.
PAST PROBLEM CASES
...
...
@@ -158,7 +154,7 @@ PAST PROBLEM CASES
succeed. It may create either WB or UC user mappings, depending
on whether the region is in kern_memmap or the EFI memory map.
mmap of 0x0-0x
A0000
/dev/mem by "hwinfo" on HP sx1000 with VGA enabled
mmap of 0x0-0x
9FFFF
/dev/mem by "hwinfo" on HP sx1000 with VGA enabled
See https://bugzilla.novell.com/show_bug.cgi?id=140858.
...
...
@@ -171,28 +167,25 @@ PAST PROBLEM CASES
so it is safe to use WB mappings.
The kernel VGA driver may ioremap the VGA frame buffer at 0xA0000,
which
will use a granule-sized UC mapping covering 0-0xFFFFF. This
granule covers some WB-only memory, but since UC is non-speculative,
the processor will never generate an uncacheable reference to the
WB-only areas unless
the driver explicitly touches them.
which
uses a granule-sized UC mapping. This granule will cover some
WB-only memory, but since UC is non-speculative, the processor will
never generate an uncacheable reference to the WB-only areas unless
the driver explicitly touches them.
mmap of 0x0-0xFFFFF legacy_mem by "X"
If the EFI memory map reports this entire range as WB, there
is no VGA MMIO hole, and the mmap should fail or be done with
a WB mapping.
If the EFI memory map reports that the entire range supports the
same attributes, we can allow the mmap (and we will prefer WB if
supported, as is the case with HP sx[12]000 machines with VGA
disabled).
There's no easy way for X to determine whether the 0xA0000-0xBFFFF
region is a frame buffer or just memory, so I think it's best to
just fail this mmap request rather than using a WB mapping. As
far as I know, there's no need to map legacy_mem with WB
mappings.
If EFI reports the range as partly WB and partly UC (as on sx[12]000
machines with VGA enabled), we must fail the mmap because there's no
safe attribute to use.
Otherwise, a UC mapping of the entire region is probably safe.
The VGA hole means the region will not be in kern_memmap. The
HP sx1000 chipset doesn't support UC access to the memory surrounding
the VGA hole, but X doesn't need that area anyway and should not
reference it.
If EFI reports some of the range but not all (as on Intel firmware
that doesn't report the VGA frame buffer at all), we should fail the
mmap and force the user to map just the specific region of interest.
mmap of 0xA0000-0xBFFFF legacy_mem by "X" on HP sx1000 with VGA disabled
...
...
@@ -202,6 +195,16 @@ PAST PROBLEM CASES
This is a special case of the previous case, and the mmap should
fail for the same reason as above.
read of /sys/devices/.../rom
For VGA devices, this may cause an ioremap() of 0xC0000. This
used to be done with a UC mapping, because the VGA frame buffer
at 0xA0000 prevents use of a WB granule. The UC mapping causes
an MCA on HP sx[12]000 chipsets.
We should use WB page table mappings to avoid covering the VGA
frame buffer.
NOTES
[1] SDM rev 2.2, vol 2, sec 4.4.1.
...
...
arch/ia64/kernel/efi.c
View file @
d2918253
...
...
@@ -660,6 +660,29 @@ efi_memory_descriptor (unsigned long phys_addr)
return
NULL
;
}
static
int
efi_memmap_intersects
(
unsigned
long
phys_addr
,
unsigned
long
size
)
{
void
*
efi_map_start
,
*
efi_map_end
,
*
p
;
efi_memory_desc_t
*
md
;
u64
efi_desc_size
;
unsigned
long
end
;
efi_map_start
=
__va
(
ia64_boot_param
->
efi_memmap
);
efi_map_end
=
efi_map_start
+
ia64_boot_param
->
efi_memmap_size
;
efi_desc_size
=
ia64_boot_param
->
efi_memdesc_size
;
end
=
phys_addr
+
size
;
for
(
p
=
efi_map_start
;
p
<
efi_map_end
;
p
+=
efi_desc_size
)
{
md
=
p
;
if
(
md
->
phys_addr
<
end
&&
efi_md_end
(
md
)
>
phys_addr
)
return
1
;
}
return
0
;
}
u32
efi_mem_type
(
unsigned
long
phys_addr
)
{
...
...
@@ -766,11 +789,28 @@ valid_phys_addr_range (unsigned long phys_addr, unsigned long size)
int
valid_mmap_phys_addr_range
(
unsigned
long
pfn
,
unsigned
long
size
)
{
unsigned
long
phys_addr
=
pfn
<<
PAGE_SHIFT
;
u64
attr
;
attr
=
efi_mem_attribute
(
phys_addr
,
size
);
/*
*
MMIO regions are often missing from the EFI memory map.
*
We must allow mmap of them for programs like X, so w
e
*
currently can't do any useful validation
.
*
/dev/mem mmap uses normal user pages, so we don't need the entire
*
granule, but the entire region we're mapping must support the sam
e
*
attribute
.
*/
if
(
attr
&
EFI_MEMORY_WB
||
attr
&
EFI_MEMORY_UC
)
return
1
;
/*
* Intel firmware doesn't tell us about all the MMIO regions, so
* in general we have to allow mmap requests. But if EFI *does*
* tell us about anything inside this region, we should deny it.
* The user can always map a smaller region to avoid the overlap.
*/
if
(
efi_memmap_intersects
(
phys_addr
,
size
))
return
0
;
return
1
;
}
...
...
arch/ia64/mm/ioremap.c
View file @
d2918253
/*
* (c) Copyright 2006 Hewlett-Packard Development Company, L.P.
* (c) Copyright 2006
, 2007
Hewlett-Packard Development Company, L.P.
* Bjorn Helgaas <bjorn.helgaas@hp.com>
*
* This program is free software; you can redistribute it and/or modify
...
...
@@ -10,51 +10,101 @@
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/efi.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/meminit.h>
static
inline
void
__iomem
*
__ioremap
(
unsigned
long
offset
,
unsigned
long
size
)
__ioremap
(
unsigned
long
phys_addr
)
{
return
(
void
__iomem
*
)
(
__IA64_UNCACHED_OFFSET
|
offset
);
return
(
void
__iomem
*
)
(
__IA64_UNCACHED_OFFSET
|
phys_addr
);
}
void
__iomem
*
ioremap
(
unsigned
long
offset
,
unsigned
long
size
)
ioremap
(
unsigned
long
phys_addr
,
unsigned
long
size
)
{
void
__iomem
*
addr
;
struct
vm_struct
*
area
;
unsigned
long
offset
;
pgprot_t
prot
;
u64
attr
;
unsigned
long
gran_base
,
gran_size
;
unsigned
long
page_base
;
/*
* For things in kern_memmap, we must use the same attribute
* as the rest of the kernel. For more details, see
* Documentation/ia64/aliasing.txt.
*/
attr
=
kern_mem_attribute
(
offset
,
size
);
attr
=
kern_mem_attribute
(
phys_addr
,
size
);
if
(
attr
&
EFI_MEMORY_WB
)
return
(
void
__iomem
*
)
phys_to_virt
(
offset
);
return
(
void
__iomem
*
)
phys_to_virt
(
phys_addr
);
else
if
(
attr
&
EFI_MEMORY_UC
)
return
__ioremap
(
offset
,
size
);
return
__ioremap
(
phys_addr
);
/*
* Some chipsets don't support UC access to memory. If
* WB is supported for the whole granule, we prefer that.
*/
gran_base
=
GRANULEROUNDDOWN
(
offset
);
gran_size
=
GRANULEROUNDUP
(
offset
+
size
)
-
gran_base
;
gran_base
=
GRANULEROUNDDOWN
(
phys_addr
);
gran_size
=
GRANULEROUNDUP
(
phys_addr
+
size
)
-
gran_base
;
if
(
efi_mem_attribute
(
gran_base
,
gran_size
)
&
EFI_MEMORY_WB
)
return
(
void
__iomem
*
)
phys_to_virt
(
offset
);
return
(
void
__iomem
*
)
phys_to_virt
(
phys_addr
);
return
__ioremap
(
offset
,
size
);
/*
* WB is not supported for the whole granule, so we can't use
* the region 7 identity mapping. If we can safely cover the
* area with kernel page table mappings, we can use those
* instead.
*/
page_base
=
phys_addr
&
PAGE_MASK
;
size
=
PAGE_ALIGN
(
phys_addr
+
size
)
-
page_base
;
if
(
efi_mem_attribute
(
page_base
,
size
)
&
EFI_MEMORY_WB
)
{
prot
=
PAGE_KERNEL
;
/*
* Mappings have to be page-aligned
*/
offset
=
phys_addr
&
~
PAGE_MASK
;
phys_addr
&=
PAGE_MASK
;
/*
* Ok, go for it..
*/
area
=
get_vm_area
(
size
,
VM_IOREMAP
);
if
(
!
area
)
return
NULL
;
area
->
phys_addr
=
phys_addr
;
addr
=
(
void
__iomem
*
)
area
->
addr
;
if
(
ioremap_page_range
((
unsigned
long
)
addr
,
(
unsigned
long
)
addr
+
size
,
phys_addr
,
prot
))
{
vunmap
((
void
__force
*
)
addr
);
return
NULL
;
}
return
(
void
__iomem
*
)
(
offset
+
(
char
__iomem
*
)
addr
);
}
return
__ioremap
(
phys_addr
);
}
EXPORT_SYMBOL
(
ioremap
);
void
__iomem
*
ioremap_nocache
(
unsigned
long
offset
,
unsigned
long
size
)
ioremap_nocache
(
unsigned
long
phys_addr
,
unsigned
long
size
)
{
if
(
kern_mem_attribute
(
offset
,
size
)
&
EFI_MEMORY_WB
)
if
(
kern_mem_attribute
(
phys_addr
,
size
)
&
EFI_MEMORY_WB
)
return
NULL
;
return
__ioremap
(
offset
,
size
);
return
__ioremap
(
phys_addr
);
}
EXPORT_SYMBOL
(
ioremap_nocache
);
void
iounmap
(
volatile
void
__iomem
*
addr
)
{
if
(
REGION_NUMBER
(
addr
)
==
RGN_GATE
)
vunmap
((
void
*
)
((
unsigned
long
)
addr
&
PAGE_MASK
));
}
EXPORT_SYMBOL
(
iounmap
);
arch/ia64/pci/pci.c
View file @
d2918253
...
...
@@ -659,8 +659,6 @@ pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma)
return
-
EINVAL
;
prot
=
phys_mem_access_prot
(
NULL
,
vma
->
vm_pgoff
,
size
,
vma
->
vm_page_prot
);
if
(
pgprot_val
(
prot
)
!=
pgprot_val
(
pgprot_noncached
(
vma
->
vm_page_prot
)))
return
-
EINVAL
;
addr
=
pci_get_legacy_mem
(
bus
);
if
(
IS_ERR
(
addr
))
...
...
include/asm-ia64/io.h
View file @
d2918253
...
...
@@ -421,11 +421,7 @@ __writeq (unsigned long val, volatile void __iomem *addr)
extern
void
__iomem
*
ioremap
(
unsigned
long
offset
,
unsigned
long
size
);
extern
void
__iomem
*
ioremap_nocache
(
unsigned
long
offset
,
unsigned
long
size
);
static
inline
void
iounmap
(
volatile
void
__iomem
*
addr
)
{
}
extern
void
iounmap
(
volatile
void
__iomem
*
addr
);
/* Use normal IO mappings for DMI */
#define dmi_ioremap ioremap
...
...
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