kboot: Add parsing of /proc/iomem into seg.c

We'll be using this code for most / all of the platforms since iomem is
the only interface that can tell us of the reserved to the linux kernel
areas that we cannot place the new kernel into, but that we are free to
use once we hit trampoline. aarch64 will use this shortly, and similar
code in amd64 will be refactored when I make that platform work.

Sponsored by:		Netflix
Reviewed by:		tsoome
Differential Revision:	https://reviews.freebsd.org/D38309
This commit is contained in:
Warner Losh 2023-02-03 08:38:14 -07:00
parent 08779e839a
commit 33e5b27254
2 changed files with 151 additions and 0 deletions

View File

@ -36,12 +36,14 @@ void hostdisk_zfs_probe(void);
bool hostdisk_zfs_find_default(void);
/* seg.c */
#define SYSTEM_RAM 1
void init_avail(void);
void need_avail(int n);
void add_avail(uint64_t start, uint64_t end, uint64_t type);
void remove_avail(uint64_t start, uint64_t end, uint64_t type);
uint64_t first_avail(uint64_t align, uint64_t min_size, uint64_t type);
void print_avail(void);
bool populate_avail_from_iomem(void);
/* util.c */
bool file2str(const char *fn, char *buffer, size_t buflen);

View File

@ -195,3 +195,152 @@ first_avail(uint64_t align, uint64_t min_size, uint64_t memtype)
return (0);
}
enum types {
system_ram = SYSTEM_RAM,
firmware_reserved,
linux_code,
linux_data,
linux_bss,
unknown,
};
static struct kv
{
uint64_t type;
char * name;
int flags;
#define KV_KEEPER 1
} str2type_kv[] = {
{ linux_code, "Kernel code", KV_KEEPER },
{ linux_data, "Kernel data", KV_KEEPER },
{ linux_bss, "Kernel bss", KV_KEEPER },
{ firmware_reserved, "reserved" },
{ 0, NULL },
};
static const char *
parse_line(const char *line, uint64_t *startp, uint64_t *endp)
{
const char *walker;
char *next;
uint64_t start, end;
/*
* Each line is a range followed by a descriptoin of the form:
* <hex-number><dash><hex-number><space><colon><space><string>
* Bail if we have any parsing errors.
*/
walker = line;
start = strtoull(walker, &next, 16);
if (start == ULLONG_MAX || walker == next)
return (NULL);
walker = next;
if (*walker != '-')
return (NULL);
walker++;
end = strtoull(walker, &next, 16);
if (end == ULLONG_MAX || walker == next)
return (NULL);
walker = next;
/* Now eat the ' : ' in front of the string we want to return */
if (strncmp(walker, " : ", 3) != 0)
return (NULL);
*startp = start;
*endp = end;
return (walker + 3);
}
static struct kv *
kvlookup(const char *str, struct kv *kvs, size_t nkv)
{
for (int i = 0; i < nkv; i++)
if (strcmp(kvs[i].name, str) == 0)
return (&kvs[i]);
return (NULL);
}
/* Trim trailing whitespace */
static void
chop(char *line)
{
char *ep = line + strlen(line) - 1;
while (ep >= line && isspace(*ep))
*ep-- = '\0';
}
#define SYSTEM_RAM_STR "System RAM"
#define RESERVED "reserved"
bool
populate_avail_from_iomem(void)
{
int fd;
char buf[128];
const char *str;
uint64_t start, end;
struct kv *kv;
fd = open("host:/proc/iomem", O_RDONLY);
if (fd == -1) {
printf("Can't get memory map\n");
return false;
}
if (fgetstr(buf, sizeof(buf), fd) < 0)
goto out; /* Nothing to do ???? */
init_avail();
chop(buf);
while (true) {
/*
* Look for top level items we understand. Skip anything that's
* a continuation, since we don't care here. If we care, we'll
* consume them all when we recognize that top level item.
*/
if (buf[0] == ' ') /* Continuation lines? Ignore */
goto next_line;
str = parse_line(buf, &start, &end);
if (str == NULL) /* Malformed -> ignore */
goto next_line;
/*
* All we care about is System RAM
*/
if (strncmp(str, SYSTEM_RAM_STR, sizeof(SYSTEM_RAM_STR) - 1) == 0)
add_avail(start, end, system_ram);
else if (strncmp(str, RESERVED, sizeof(RESERVED) - 1) == 0)
add_avail(start, end, firmware_reserved);
else
goto next_line; /* Ignore hardware */
while (fgetstr(buf, sizeof(buf), fd) >= 0 && buf[0] == ' ') {
chop(buf);
str = parse_line(buf, &start, &end);
if (str == NULL)
break;
kv = kvlookup(str, str2type_kv, nitems(str2type_kv));
if (kv == NULL) /* failsafe for new types: igonre */
remove_avail(start, end, unknown);
else if ((kv->flags & KV_KEEPER) == 0)
remove_avail(start, end, kv->type);
/* Else no need to adjust since it's a keeper */
}
/*
* if buf[0] == ' ' then we know that the fgetstr failed and we
* should break. Otherwise fgetstr succeeded and we have a
* buffer we need to examine for being a top level item.
*/
if (buf[0] == ' ')
break;
chop(buf);
continue; /* buf has next top level line to parse */
next_line:
if (fgetstr(buf, sizeof(buf), fd) < 0)
break;
}
out:
close(fd);
return true;
}