1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

powerpc64: Avoid overwriting initrd area

Currently kexec loads an initrd file into the main memory but does not
mark that region as reserved, thus the area is not protected.

If any initrd/md file is loaded from kexec/petitboot, the region might become
corarupted/overwritten since FreeBSD does not know the region is 'reserved'.

This patch simply adds the initrd area as a reserved memory region.

Approved by: jhibbits
Differential Revision: https://reviews.freebsd.org/D15610
This commit is contained in:
Breno Leitao 2018-06-01 12:43:13 +00:00
parent 57a865f808
commit 48f64992f2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=334485

View File

@ -225,41 +225,19 @@ parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
#ifdef FDT
static int
excise_fdt_reserved(struct mem_region *avail, int asz)
excise_reserved_regions(struct mem_region *avail, int asz,
struct mem_region *exclude, int esz)
{
struct {
uint64_t address;
uint64_t size;
} fdtmap[32];
ssize_t fdtmapsize;
phandle_t chosen;
int i, j, k;
chosen = OF_finddevice("/chosen");
fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap));
for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) {
fdtmap[j].address = be64toh(fdtmap[j].address) & ~PAGE_MASK;
fdtmap[j].size = round_page(be64toh(fdtmap[j].size));
}
KASSERT(j*sizeof(fdtmap[0]) < sizeof(fdtmap),
("Exceeded number of FDT reservations"));
/* Add a virtual entry for the FDT itself */
if (fdt != NULL) {
fdtmap[j].address = (vm_offset_t)fdt & ~PAGE_MASK;
fdtmap[j].size = round_page(fdt_totalsize(fdt));
fdtmapsize += sizeof(fdtmap[0]);
}
for (i = 0; i < asz; i++) {
for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) {
for (j = 0; j < esz; j++) {
/*
* Case 1: Exclusion region encloses complete
* available entry. Drop it and move on.
*/
if (fdtmap[j].address <= avail[i].mr_start &&
fdtmap[j].address + fdtmap[j].size >=
if (exclude[j].mr_start <= avail[i].mr_start &&
exclude[j].mr_start + exclude[j].mr_size >=
avail[i].mr_start + avail[i].mr_size) {
for (k = i+1; k < asz; k++)
avail[k-1] = avail[k];
@ -274,20 +252,20 @@ excise_fdt_reserved(struct mem_region *avail, int asz)
* a new available entry with the region after
* the excluded region, if any.
*/
if (fdtmap[j].address >= avail[i].mr_start &&
fdtmap[j].address < avail[i].mr_start +
if (exclude[j].mr_start >= avail[i].mr_start &&
exclude[j].mr_start < avail[i].mr_start +
avail[i].mr_size) {
if (fdtmap[j].address + fdtmap[j].size <
if (exclude[j].mr_start + exclude[j].mr_size <
avail[i].mr_start + avail[i].mr_size) {
avail[asz].mr_start =
fdtmap[j].address + fdtmap[j].size;
exclude[j].mr_start + exclude[j].mr_size;
avail[asz].mr_size = avail[i].mr_start +
avail[i].mr_size -
avail[asz].mr_start;
asz++;
}
avail[i].mr_size = fdtmap[j].address -
avail[i].mr_size = exclude[j].mr_start -
avail[i].mr_start;
}
@ -297,13 +275,13 @@ excise_fdt_reserved(struct mem_region *avail, int asz)
* The case of a contained exclusion zone has already
* been caught in case 2.
*/
if (fdtmap[j].address + fdtmap[j].size >=
avail[i].mr_start && fdtmap[j].address +
fdtmap[j].size < avail[i].mr_start +
if (exclude[j].mr_start + exclude[j].mr_size >=
avail[i].mr_start && exclude[j].mr_start +
exclude[j].mr_size < avail[i].mr_start +
avail[i].mr_size) {
avail[i].mr_size += avail[i].mr_start;
avail[i].mr_start =
fdtmap[j].address + fdtmap[j].size;
exclude[j].mr_start + exclude[j].mr_size;
avail[i].mr_size -= avail[i].mr_start;
}
}
@ -311,6 +289,62 @@ excise_fdt_reserved(struct mem_region *avail, int asz)
return (asz);
}
static int
excise_initrd_region(struct mem_region *avail, int asz)
{
phandle_t chosen;
uint64_t start, end;
ssize_t size;
struct mem_region initrdmap[1];
chosen = OF_finddevice("/chosen");
size = OF_getprop(chosen, "linux,initrd-start", &start, sizeof(start));
if (size <= 0)
return (asz);
size = OF_getprop(chosen, "linux,initrd-end", &end, sizeof(end));
if (size <= 0)
return (asz);
initrdmap[0].mr_start = start;
initrdmap[0].mr_size = end - start;
asz = excise_reserved_regions(avail, asz, initrdmap, 1);
return (asz);
}
static int
excise_fdt_reserved(struct mem_region *avail, int asz)
{
struct mem_region fdtmap[32];
ssize_t fdtmapsize;
phandle_t chosen;
int j, fdtentries;
chosen = OF_finddevice("/chosen");
fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap));
for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) {
fdtmap[j].mr_start = be64toh(fdtmap[j].mr_start) & ~PAGE_MASK;
fdtmap[j].mr_size = round_page(be64toh(fdtmap[j].mr_size));
}
KASSERT(j*sizeof(fdtmap[0]) < sizeof(fdtmap),
("Exceeded number of FDT reservations"));
/* Add a virtual entry for the FDT itself */
if (fdt != NULL) {
fdtmap[j].mr_start = (vm_offset_t)fdt & ~PAGE_MASK;
fdtmap[j].mr_size = round_page(fdt_totalsize(fdt));
fdtmapsize += sizeof(fdtmap[0]);
}
fdtentries = fdtmapsize/sizeof(fdtmap[0]);
asz = excise_reserved_regions(avail, asz, fdtmap, fdtentries);
return (asz);
}
#endif
/*
@ -364,6 +398,13 @@ ofw_mem_regions(struct mem_region *memp, int *memsz,
phandle = OF_finddevice("/chosen");
if (OF_hasprop(phandle, "fdtmemreserv"))
asz = excise_fdt_reserved(availp, asz);
/* If the kernel is being loaded through kexec, initrd region is listed
* in /chosen but the region is not marked as reserved, so, we might exclude
* it here.
*/
if (OF_hasprop(phandle, "linux,initrd-start"))
asz = excise_initrd_region(availp, asz);
#endif
*memsz = msz;