From 23aa6f84539ba320a91235fb81681d0754178f62 Mon Sep 17 00:00:00 2001
From: Marvin Borner
Date: Mon, 5 Jul 2021 23:22:24 +0200
Subject: ACPI and config parsing

---
 src/loader/acpi.c         |  92 ++++++++++++++++++++++++
 src/loader/cfg.c          | 173 ++++++++++++++++++++++++++++++++++++++++++++++
 src/loader/dev.c          |  10 +++
 src/loader/fs/ext2.c      |   2 +-
 src/loader/impl/mb1.c     |   9 +++
 src/loader/inc/acpi.h     | 168 ++++++++++++++++++++++++++++++++++++++++++++
 src/loader/inc/cfg.h      |  22 ++++++
 src/loader/inc/def.h      |   1 +
 src/loader/inc/dev.h      |   1 +
 src/loader/inc/impl/mb1.h |  13 ++++
 src/loader/inc/lib.h      |   4 +-
 src/loader/lib.c          |  36 ++++++++--
 src/loader/main.c         |   6 ++
 13 files changed, 529 insertions(+), 8 deletions(-)
 create mode 100644 src/loader/acpi.c
 create mode 100644 src/loader/cfg.c
 create mode 100644 src/loader/impl/mb1.c
 create mode 100644 src/loader/inc/acpi.h
 create mode 100644 src/loader/inc/cfg.h
 create mode 100644 src/loader/inc/impl/mb1.h

(limited to 'src/loader')

diff --git a/src/loader/acpi.c b/src/loader/acpi.c
new file mode 100644
index 0000000..f7e958c
--- /dev/null
+++ b/src/loader/acpi.c
@@ -0,0 +1,92 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <acpi.h>
+#include <lib.h>
+#include <pnc.h>
+
+/**
+ * General SDP
+ */
+
+static u8 acpi_sdp_verify(struct sdp_header *header)
+{
+	u8 sum = 0;
+
+	for (u32 i = 0; i < sizeof(struct rsdp); i++)
+		sum += (u8)(((u8 *)header)[i]);
+
+	return sum == 0;
+}
+
+/**
+ * General SDT
+ */
+
+static u8 acpi_sdt_verify(struct sdt_header *header)
+{
+	u8 sum = 0;
+
+	for (u32 i = 0; i < header->length; i++)
+		sum += (u8)(((u8 *)header)[i]);
+
+	return sum == 0;
+}
+
+static void *acpi_sdt_find(struct rsdt *rsdt, const char *signature)
+{
+	u32 entries = (rsdt->header.length - sizeof(rsdt->header)) / 4;
+
+	for (u32 i = 0; i < entries; i++) {
+		struct sdt_header *header = (struct sdt_header *)rsdt->sdt_pointer[i];
+		if (memcmp(header->signature, signature, 4) == 0) {
+			if (acpi_sdt_verify(header))
+				return header;
+			else
+				break;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * RSDP - points to RSDT
+ */
+
+static struct rsdp *acpi_rsdp_find(void)
+{
+	// Main BIOS area
+	for (u32 i = 0xe0000; i < 0xfffff; i++)
+		if (memcmp((u32 *)i, RSDP_MAGIC, 8) == 0)
+			return (struct rsdp *)i;
+
+	// Or first KB of EBDA?
+	u8 *ebda = (void *)(*((u16 *)0x40e) << 4);
+	for (u16 i = 0; i < 1024; i += 16)
+		if (memcmp(ebda + i, RSDP_MAGIC, 8) == 0)
+			return (struct rsdp *)(ebda + i);
+
+	return NULL;
+}
+
+/**
+ * Probe
+ */
+
+void acpi_probe(void)
+{
+	struct rsdp *rsdp = acpi_rsdp_find();
+	assert(rsdp && rsdp->header.revision == 0 && acpi_sdp_verify(&rsdp->header));
+	struct rsdt *rsdt = rsdp->rsdt;
+	assert(rsdt && memcmp(rsdt->header.signature, RSDT_MAGIC, 4) == 0 &&
+	       acpi_sdt_verify(&rsdt->header));
+
+	struct madt *madt = acpi_sdt_find(rsdt, MADT_MAGIC);
+	struct fadt *fadt = acpi_sdt_find(rsdt, FADT_MAGIC);
+	struct hpet *hpet = acpi_sdt_find(rsdt, HPET_MAGIC);
+
+	// TODO!
+	UNUSED(madt);
+	UNUSED(fadt);
+	UNUSED(hpet);
+}
diff --git a/src/loader/cfg.c b/src/loader/cfg.c
new file mode 100644
index 0000000..37794e6
--- /dev/null
+++ b/src/loader/cfg.c
@@ -0,0 +1,173 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <cfg.h>
+#include <dev.h>
+#include <lib.h>
+#include <log.h>
+#include <pnc.h>
+
+// Keys
+#define TIMEOUT "TIMEOUT"
+#define PATH "PATH"
+
+// Config struct (gettable using cfg_get)
+struct {
+	u32 timeout;
+	struct {
+		u8 exists : 1;
+		char name[64];
+		char path[64];
+	} elem[16]; // Up to 16 different selections
+} cfg = { 0 };
+
+// Config file contents (if found)
+static char file[1024] = { 0 };
+
+// Find config file
+static u8 cfg_find(struct dev *dev)
+{
+	if (!dev->fs.read)
+		return 0; // No fs found or not readable - continue!
+
+	s32 res = dev->fs.read("/boot/segelboot.cfg", file, 0, sizeof(file), dev);
+	if (res > 0)
+		return 1; // Break foreach
+
+	// Continue foreach
+	return 0;
+}
+
+// Checks if index is appropriate as some key/value need to be in element
+static void cfg_in_element(u8 index)
+{
+	if (index == 0xff)
+		panic("No element name given\n");
+}
+
+// Add/overwrite value by key and element index
+static void cfg_add(u8 index, enum cfg_key key, const char *value)
+{
+	cfg.elem[index].exists = 1;
+
+	switch (key) {
+	case CFG_NAME:
+		cfg_in_element(index);
+		strlcpy(cfg.elem[index].name, value, sizeof(cfg.elem[index].name));
+		break;
+	case CFG_TIMEOUT:
+		cfg.timeout = atoi(value);
+		break;
+	case CFG_PATH:
+		cfg_in_element(index);
+		strlcpy(cfg.elem[index].path, value, sizeof(cfg.elem[index].path));
+		break;
+	case CFG_NONE:
+	default:
+		panic("Invalid config\n");
+	}
+}
+
+// TODO: This code is kind of messy
+// Structure per line: KEY=VALUE
+static void cfg_parse(void)
+{
+	// Element index
+	u8 elem = 0xff;
+
+	// Value per key
+	char value[64] = { 0 };
+	u8 value_index = 0;
+
+	// States
+	enum cfg_key current = CFG_NONE; // Value key type
+	u8 state = 0; // 0 is key, 1 is value, 2 is elem
+
+	const char *start = file; // Start is at the beginning of the key
+	for (const char *p = start; *p; p++) {
+		if (state == 0) {
+			// We're at key parsing
+			if (*p == '\n') { // A key can't just end but ok
+				start = p + 1;
+			} else if (*p == '#') {
+				state = 2; // Let's parse the element name
+				p++;
+				continue;
+			} else if (*p != '=') {
+				continue;
+			}
+
+			// The key is now at start until p
+			u8 diff = p - start;
+
+			// Timeout key
+			if (diff == sizeof(TIMEOUT) - 1 && memcmp(start, TIMEOUT, diff) == 0) {
+				current = CFG_TIMEOUT;
+				state = 1;
+				continue;
+			}
+
+			// Path key
+			if (diff == sizeof(PATH) - 1 && memcmp(start, PATH, diff) == 0) {
+				current = CFG_PATH;
+				state = 1;
+				continue;
+			}
+		} else if (state == 1) {
+			// We're at value parsing
+			assert(value_index + 1 < (u8)sizeof(value));
+			if (*p == '\n') { // Finished
+				value[value_index] = 0;
+				cfg_add(elem, current, value);
+				value_index = 0;
+				state = 0;
+				p--; // Repeat parse normally
+			} else {
+				value[value_index++] = *p;
+			}
+		} else if (state == 2) {
+			// We're at element name parsing
+			assert(value_index + 1 < (u8)sizeof(value));
+			if (*p == '\n') { // Finished
+				elem = elem == 0xff ? 0 : elem + 1;
+				value[value_index] = 0;
+				cfg_add(elem, CFG_NAME, value);
+				value_index = 0;
+				state = 0;
+				p--; // Repeat parse normally
+			} else {
+				value[value_index++] = *p;
+			}
+		}
+	}
+}
+
+const void *cfg_get(u8 index, enum cfg_key key)
+{
+	switch (key) {
+	case CFG_NAME:
+		return &cfg.elem[index].path;
+	case CFG_TIMEOUT:
+		return &cfg.timeout;
+	case CFG_PATH:
+		return &cfg.elem[index].path;
+	case CFG_NONE:
+	default:
+		return NULL;
+	}
+}
+
+void cfg_print(void)
+{
+	log("[CFG] Global: %d\n", cfg.timeout);
+
+	for (u8 i = 0; i < COUNT(cfg.elem) && cfg.elem[i].exists; i++)
+		log("[CFG] Element: %s at %s\n", cfg.elem[i].name, cfg.elem[i].path);
+}
+
+void cfg_exec(void)
+{
+	dev_foreach(DEV_DISK, &cfg_find);
+	if (!file[0])
+		panic("No config found\n");
+	cfg_parse();
+}
diff --git a/src/loader/dev.c b/src/loader/dev.c
index e529dfa..591d5a2 100644
--- a/src/loader/dev.c
+++ b/src/loader/dev.c
@@ -25,6 +25,16 @@ struct dev *dev_get(u8 id)
 	return &devices[id];
 }
 
+void dev_foreach(enum dev_type type, u8 (*cb)(struct dev *))
+{
+	for (u8 i = 0; i < COUNT(devices); i++) {
+		struct dev *dev = &devices[i];
+		if (dev->type == type)
+			if (cb(dev))
+				break;
+	}
+}
+
 u8 dev_register(enum dev_type type, char *name, u32 data,
 		s32 (*read)(void *, u32, u32, struct dev *),
 		s32 (*write)(const void *, u32, u32, struct dev *))
diff --git a/src/loader/fs/ext2.c b/src/loader/fs/ext2.c
index fd46ca6..a7d4662 100644
--- a/src/loader/fs/ext2.c
+++ b/src/loader/fs/ext2.c
@@ -134,7 +134,7 @@ static u32 ext2_find_inode(const char *name, u32 dir_inode, struct dev *dev)
 		}
 		d = (struct ext2_dirent *)((u32)d + d->total_len);
 
-	} while (sum < (1024 * i.blocks / 2));
+	} while (sum < (BLOCK_SIZE * i.blocks / 2));
 
 	return (unsigned)-1;
 }
diff --git a/src/loader/impl/mb1.c b/src/loader/impl/mb1.c
new file mode 100644
index 0000000..5265082
--- /dev/null
+++ b/src/loader/impl/mb1.c
@@ -0,0 +1,9 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <impl/mb1.h>
+
+u8 mb1_probe(struct dev *dev)
+{
+	(void)dev;
+	return 1;
+}
diff --git a/src/loader/inc/acpi.h b/src/loader/inc/acpi.h
new file mode 100644
index 0000000..f7626e9
--- /dev/null
+++ b/src/loader/inc/acpi.h
@@ -0,0 +1,168 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+// Many huge structures from ACPI spec
+
+#ifndef ACPI_H
+#define ACPI_H
+
+#include <def.h>
+
+#define RSDP_MAGIC "RSD PTR "
+#define RSDT_MAGIC "RSDT"
+#define MADT_MAGIC "APIC"
+#define FADT_MAGIC "FACP"
+#define HPET_MAGIC "HPET"
+
+/**
+ * General headers
+ */
+
+struct sdt_header {
+	u8 signature[4];
+	u32 length;
+	u8 revision;
+	u8 checksum;
+	u8 oem_id[6];
+	u8 oem_table_id[8];
+	u32 oem_revision;
+	u32 creator_id;
+	u32 creator_revision;
+};
+
+struct sdp_header {
+	u8 signature[8];
+	u8 checksum;
+	u8 oem_id[6];
+	u8 revision;
+};
+
+struct generic_address {
+	u8 address_space;
+	u8 bit_width;
+	u8 bit_offset;
+	u8 access_size;
+	u32 phys_high;
+	u32 phys_low;
+};
+
+/**
+ * RSDT
+ */
+
+struct rsdt {
+	struct sdt_header header;
+	u32 sdt_pointer[];
+};
+
+/**
+ * RSDP - points to RSDT
+ */
+
+struct rsdp {
+	struct sdp_header header;
+	struct rsdt *rsdt;
+};
+
+/**
+ * MADT
+ */
+
+struct madt_entry_header {
+	u8 type;
+	u8 length;
+};
+
+struct madt {
+	struct sdt_header header;
+	u32 local_address;
+	u32 flags;
+	struct madt_entry_header entry;
+} PACKED;
+
+/**
+ * FADT
+ */
+
+struct fadt {
+	struct sdt_header header;
+	u32 firmware_ctrl;
+	u32 dsdt;
+	u8 reserved;
+	u8 preferred_power_mgmt;
+	u16 sci_interrupt;
+	u32 smi_command_port;
+	u8 acpi_enable;
+	u8 acpi_disable;
+	u8 s4_bios_req;
+	u8 pstate_control;
+	u32 pm1a_event_block;
+	u32 pm1b_event_block;
+	u32 pm1a_control_block;
+	u32 pm1b_control_block;
+	u32 pm2_control_block;
+	u32 pm_timer_block;
+	u32 gpe0_block;
+	u32 gpe1_block;
+	u8 pm1_event_length;
+	u8 pm1_control_length;
+	u8 pm2_control_length;
+	u8 pm_timer_length;
+	u8 gpe0_length;
+	u8 gpe1_length;
+	u8 gpe1_base;
+	u8 c_state_control;
+	u16 worst_c2_latency;
+	u16 worst_c3_latency;
+	u16 flush_size;
+	u16 flush_stride;
+	u8 duty_offset;
+	u8 duty_width;
+	u8 day_alarm;
+	u8 month_alarm;
+	u8 century;
+
+	// Used since ACPI 2.0+
+	u16 boot_architecture_flags;
+
+	u8 reserved2;
+	u32 flags;
+	struct generic_address reset_reg;
+	u8 reset_value;
+	u8 reserved3[3];
+
+	// Available on ACPI 2.0+
+	u32 x_firmware_control_high;
+	u32 x_firmware_control_low;
+	u32 x_dsdt_high;
+	u32 x_dsdt_low;
+
+	struct generic_address x_pm1a_event_block;
+	struct generic_address x_pm1b_event_block;
+	struct generic_address x_pm1a_control_block;
+	struct generic_address x_pm1b_control_block;
+	struct generic_address x_pm2_control_block;
+	struct generic_address x_pm_timer_block;
+	struct generic_address x_gpe0_block;
+	struct generic_address x_gpe1_block;
+} PACKED;
+
+/**
+ * HPET
+ */
+
+struct hpet {
+	struct sdt_header header;
+	u8 hardware_rev_id;
+	u8 comparator_count : 5;
+	u8 counter_size : 1;
+	u8 reserved : 1;
+	u8 legacy_replacement : 1;
+	u16 pci_vendor_id;
+	struct generic_address address;
+	u8 hpet_number;
+	u16 minimum_tick;
+	u8 page_protection;
+} PACKED;
+
+void acpi_probe(void);
+
+#endif
diff --git a/src/loader/inc/cfg.h b/src/loader/inc/cfg.h
new file mode 100644
index 0000000..a214db2
--- /dev/null
+++ b/src/loader/inc/cfg.h
@@ -0,0 +1,22 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#ifndef CFG_H
+#define CFG_H
+
+#include <def.h>
+
+// Global config element index (can actually be anything but wth)
+#define CFG_GLOBAL 0
+
+enum cfg_key {
+	CFG_NONE,
+	CFG_NAME,
+	CFG_TIMEOUT,
+	CFG_PATH,
+};
+
+const void *cfg_get(u8 index, enum cfg_key key);
+void cfg_exec(void);
+void cfg_print(void);
+
+#endif
diff --git a/src/loader/inc/def.h b/src/loader/inc/def.h
index cb41c78..3532bc1 100644
--- a/src/loader/inc/def.h
+++ b/src/loader/inc/def.h
@@ -23,6 +23,7 @@ typedef __builtin_va_list va_list;
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 #define ABS(a) (((a) < 0) ? -(a) : (a))
 #define COUNT(a) (sizeof(a) / sizeof 0 [a])
+#define UNUSED(a) ((void)a);
 
 #define PACKED __attribute__((packed))
 
diff --git a/src/loader/inc/dev.h b/src/loader/inc/dev.h
index 6fcbf25..9d9913e 100644
--- a/src/loader/inc/dev.h
+++ b/src/loader/inc/dev.h
@@ -25,6 +25,7 @@ struct dev {
 };
 
 struct dev *dev_get(u8 id);
+void dev_foreach(enum dev_type type, u8 (*cb)(struct dev *)); // cb=1 => break
 u8 dev_register(enum dev_type type, char *name, u32 data,
 		s32 (*read)(void *, u32, u32, struct dev *),
 		s32 (*write)(const void *, u32, u32, struct dev *));
diff --git a/src/loader/inc/impl/mb1.h b/src/loader/inc/impl/mb1.h
new file mode 100644
index 0000000..fa0efc1
--- /dev/null
+++ b/src/loader/inc/impl/mb1.h
@@ -0,0 +1,13 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#ifndef MB1_H
+#define MB1_H
+
+#include <def.h>
+#include <dev.h>
+
+#define MB1_MAGIC 0x1badb002
+
+u8 mb1_probe(struct dev *dev);
+
+#endif
diff --git a/src/loader/inc/lib.h b/src/loader/inc/lib.h
index e811670..2f49007 100644
--- a/src/loader/inc/lib.h
+++ b/src/loader/inc/lib.h
@@ -12,7 +12,9 @@ s32 strncmp(const char *s1, const char *s2, u32 n);
 
 void *memcpy(void *dest, const void *src, u32 n);
 void *memset(void *dest, u32 val, u32 n);
+s32 memcmp(const void *s1, const void *s2, u32 n);
 
-int itoa(s32 value, char *buffer, u32 base);
+s32 itoa(s32 value, char *buffer, u32 base);
+s32 atoi(const char *inp);
 
 #endif
diff --git a/src/loader/lib.c b/src/loader/lib.c
index 83b1e25..a5a1aee 100644
--- a/src/loader/lib.c
+++ b/src/loader/lib.c
@@ -49,10 +49,10 @@ s32 strncmp(const char *s1, const char *s2, u32 n)
 	const u8 *c1 = (const u8 *)s1;
 	const u8 *c2 = (const u8 *)s2;
 	u8 ch;
-	int d = 0;
+	s32 d = 0;
 
 	while (n--) {
-		d = (int)(ch = *c1++) - (int)*c2++;
+		d = (s32)(ch = *c1++) - (s32)*c2++;
 		if (d || !ch)
 			break;
 	}
@@ -107,18 +107,31 @@ void *memset(void *dest, u32 val, u32 n)
 	return dest;
 }
 
+s32 memcmp(const void *s1, const void *s2, u32 n)
+{
+	const u8 *a = (const u8 *)s1;
+	const u8 *b = (const u8 *)s2;
+	for (u32 i = 0; i < n; i++) {
+		if (a[i] < b[i])
+			return -1;
+		else if (b[i] < a[i])
+			return 1;
+	}
+	return 0;
+}
+
 /**
  * Conversion
  */
 
-int itoa(s32 value, char *buffer, u32 base)
+s32 itoa(s32 value, char *buffer, u32 base)
 {
 	char tmp[16];
 	char *tp = tmp;
-	int i;
+	s32 i;
 	unsigned v;
 
-	int sign = (base == 10 && value < 0);
+	s32 sign = (base == 10 && value < 0);
 	if (sign)
 		v = -value;
 	else
@@ -133,7 +146,7 @@ int itoa(s32 value, char *buffer, u32 base)
 			*tp++ = i + 'a' - 10;
 	}
 
-	int len = tp - tmp;
+	s32 len = tp - tmp;
 
 	if (sign) {
 		*buffer++ = '-';
@@ -146,3 +159,14 @@ int itoa(s32 value, char *buffer, u32 base)
 
 	return len;
 }
+
+// From stackoverflow
+s32 atoi(const char *inp)
+{
+	s32 ret = 0;
+	while (*inp) {
+		ret = (ret << 3) + (ret << 1) + (*inp) - '0';
+		inp++;
+	}
+	return ret;
+}
diff --git a/src/loader/main.c b/src/loader/main.c
index 7c50e62..f01a93b 100644
--- a/src/loader/main.c
+++ b/src/loader/main.c
@@ -1,5 +1,7 @@
 // MIT License, Copyright (c) 2021 Marvin Borner
 
+#include <acpi.h>
+#include <cfg.h>
 #include <def.h>
 #include <dev.h>
 #include <ide.h>
@@ -21,9 +23,13 @@ int start(void)
 	pic_install();
 	idt_install();
 
+	acpi_probe();
 	pci_probe();
 	dev_print();
 
+	cfg_exec();
+	cfg_print();
+
 	// Sleep and wait for interrupts
 	while (1)
 		__asm__ volatile("hlt");
-- 
cgit v1.2.3