#include "stand.h"
#include "seg.h"
#include <sys/param.h>
static struct memory_segments *segs;
static int nr_seg = 0;
static int segalloc = 0;
void
init_avail(void)
{
if (segs)
free(segs);
nr_seg = 0;
segalloc = 16;
free(segs);
segs = malloc(sizeof(*segs) * segalloc);
if (segs == NULL)
panic("not enough memory to get memory map\n");
}
void
need_avail(int n)
{
if (n <= segalloc)
return;
while (n > segalloc)
segalloc *= 2;
segs = realloc(segs, segalloc * sizeof(*segs));
if (segs == NULL)
panic("not enough memory to get memory map\n");
}
void
add_avail(uint64_t start, uint64_t end, uint64_t type)
{
if (nr_seg >= 1 &&
segs[nr_seg - 1].end + 1 == start &&
segs[nr_seg - 1].type == type) {
segs[nr_seg - 1].end = end;
return;
}
need_avail(nr_seg + 1);
segs[nr_seg].start = start;
segs[nr_seg].end = end;
segs[nr_seg].type = type;
nr_seg++;
}
void
remove_avail(uint64_t start, uint64_t end, uint64_t type)
{
struct memory_segments *s;
if (nr_seg >= 2) {
s = &segs[nr_seg - 2];
if (s->end + 1 == start &&
s->type == type) {
s->end = end;
s++;
if (s->end == end) {
nr_seg--;
return;
}
s->start = end + 1;
return;
}
}
s = &segs[nr_seg - 1];
if (s->start == start) {
if (s->end == end) {
s->type = type;
return;
}
need_avail(nr_seg + 1);
s = &segs[nr_seg - 1];
s[1] = s[0];
s->start = start;
s->end = end;
s->type = type;
s[1].start = end + 1;
nr_seg++;
return;
}
if (s->end == end) {
need_avail(nr_seg + 1);
s = &segs[nr_seg - 1];
s[1] = s[0];
s->end = start - 1;
s[1].start = start;
s[1].type = type;
nr_seg++;
return;
}
need_avail(nr_seg + 2);
s = &segs[nr_seg - 1];
s[2] = s[1] = s[0];
s->end = start - 1;
s[1].start = start;
s[1].end = end;
s[1].type = type;
s[2].start = end + 1;
nr_seg += 2;
}
void
print_avail(void)
{
printf("Found %d RAM segments:\n", nr_seg);
for (int i = 0; i < nr_seg; i++) {
printf("%#jx-%#jx type %lu\n",
(uintmax_t)segs[i].start,
(uintmax_t)segs[i].end,
(u_long)segs[i].type);
}
}
uint64_t
first_avail(uint64_t align, uint64_t min_size, uint64_t memtype)
{
uint64_t s, len;
for (int i = 0; i < nr_seg; i++) {
if (segs[i].type != memtype)
continue;
s = roundup(segs[i].start, align);
if (s >= segs[i].end)
continue;
len = segs[i].end - s + 1;
if (len >= min_size) {
printf("Found a big enough hole at in seg %d at %#jx (%#jx-%#jx)\n",
i,
(uintmax_t)s,
(uintmax_t)segs[i].start,
(uintmax_t)segs[i].end);
return (s);
}
}
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" },
};
static const char *
parse_line(const char *line, uint64_t *startp, uint64_t *endp)
{
const char *walker;
char *next;
uint64_t start, end;
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;
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);
}
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");
init_avail();
add_avail(4ull << 30, 36ull << 30, system_ram);
return false;
}
if (fgetstr(buf, sizeof(buf), fd) < 0)
goto out;
init_avail();
chop(buf);
while (true) {
if (buf[0] == ' ')
goto next_line;
str = parse_line(buf, &start, &end);
if (str == NULL)
goto next_line;
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;
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)
remove_avail(start, end, unknown);
else if ((kv->flags & KV_KEEPER) == 0)
remove_avail(start, end, kv->type);
}
if (buf[0] == ' ')
break;
chop(buf);
continue;
next_line:
if (fgetstr(buf, sizeof(buf), fd) < 0)
break;
}
out:
close(fd);
return true;
}
uint64_t
space_avail(uint64_t start)
{
for (int i = 0; i < nr_seg; i++) {
if (start >= segs[i].start && start <= segs[i].end)
return segs[i].end - start;
}
return 0;
}