From 33e5b27254e78c45a3cb728243709c16100fac36 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 3 Feb 2023 08:38:14 -0700 Subject: [PATCH] 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 --- stand/kboot/kboot.h | 2 + stand/kboot/seg.c | 149 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/stand/kboot/kboot.h b/stand/kboot/kboot.h index 2d89933b51a..dcf1487ce40 100644 --- a/stand/kboot/kboot.h +++ b/stand/kboot/kboot.h @@ -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); diff --git a/stand/kboot/seg.c b/stand/kboot/seg.c index 41ded9b4083..8cf3b833c9d 100644 --- a/stand/kboot/seg.c +++ b/stand/kboot/seg.c @@ -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: + * + * 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; +}