Browse Source

Properly detect memory using atags and add it to the memory-management subsystem

Aaron Lindsay 7 years ago
parent
commit
afba079256
8 changed files with 171 additions and 24 deletions
  1. 48 0
      include/atags.h
  2. 2 1
      include/mmu.h
  3. 1 0
      include/types.h
  4. 1 0
      kernel/Makefile.inc
  5. 42 0
      kernel/atags.c
  6. 8 7
      kernel/mm.c
  7. 55 9
      kernel/mmu.c
  8. 14 7
      kernel/start_kernel.c

+ 48 - 0
include/atags.h

@@ -0,0 +1,48 @@
1
+#ifndef ATAGS_H
2
+#define ATAGS_H
3
+
4
+#include <types.h>
5
+
6
+#define ATAG_NONE	0x00000000
7
+#define ATAG_CORE	0x54410001
8
+#define ATAG_MEM	0x54410002
9
+#define ATAG_VIDEOTEXT	0x54410003
10
+#define ATAG_RAMDISK	0x54410004
11
+#define ATAG_INITRD2	0x54420005
12
+#define ATAG_SERIAL	0x54410006
13
+#define ATAG_REVISION	0x54410007
14
+#define ATAG_VIDEOLFB	0x54410008
15
+#define ATAG_CMDLINE	0x54410009
16
+
17
+struct atag_core {
18
+	uint32 flags;		/* bit 0 = read-only */
19
+	uint32 pagesize;	/* systems page size (usually 4k) */
20
+	uint32 rootdev;		/* root device number */
21
+};
22
+
23
+struct atag_mem {
24
+	uint32 size;	/* size of the area */
25
+	uint32 start;	/* physical start address */
26
+};
27
+
28
+struct atag {
29
+	uint32 size; /* Size of tag in words (includes entire struct) */
30
+	uint32 tag;
31
+	union {
32
+		struct atag_core	core;
33
+		struct atag_mem		mem;
34
+//		struct atag_videotext	videotext;
35
+//		struct atag_ramdisk	ramdisk;
36
+//		struct atag_initrd2	initrd2;
37
+//		struct atag_serialnr	serialnr;
38
+//		struct atag_revision	revision;
39
+//		struct atag_videolfb	videolfb;
40
+//		struct atag_cmdline	cmdline;
41
+	} data;
42
+};
43
+
44
+#define atags_first_mem_region(atag) _atags_mem_region(atag, 1)
45
+#define atags_next_mem_region(atag) _atags_mem_region(atag, 0)
46
+int _atags_mem_region(struct atag **mem_header, int initialize);
47
+
48
+#endif /* ATAGS_H */

+ 2 - 1
include/mmu.h

@@ -1,8 +1,9 @@
1 1
 #ifndef MMU_H
2 2
 #define MMU_H
3 3
 
4
-extern unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt;
4
+#include <types.h>
5 5
 
6 6
 void mmu_reinit();
7
+void declare_memory_region(void *lower, void *upper);
7 8
 
8 9
 #endif /* MMU_H */

+ 1 - 0
include/types.h

@@ -0,0 +1 @@
1
+typedef unsigned int uint32;

+ 1 - 0
kernel/Makefile.inc

@@ -1,5 +1,6 @@
1 1
 KERNEL_PREFIX = kernel
2 2
 
3
+KOBJS += $(KERNEL_PREFIX)/atags.o
3 4
 KOBJS += $(KERNEL_PREFIX)/console.o
4 5
 KOBJS += $(KERNEL_PREFIX)/font.o
5 6
 KOBJS += $(KERNEL_PREFIX)/framebuffer.o

+ 42 - 0
kernel/atags.c

@@ -0,0 +1,42 @@
1
+#include <atags.h>
2
+
3
+extern uint32 atags_ptr;
4
+
5
+int atag_valid(struct atag *a) {
6
+	switch (a->tag) {
7
+		case ATAG_NONE:
8
+			return a->size == 2;
9
+		case ATAG_CORE:
10
+			return a->size == 2 || a->size == 5;
11
+		case ATAG_MEM:
12
+		case ATAG_INITRD2:
13
+		case ATAG_SERIAL:
14
+			return a->size == 4;
15
+		case ATAG_VIDEOTEXT:
16
+		case ATAG_RAMDISK:
17
+			return a->size == 5;
18
+		case ATAG_REVISION:
19
+			return a->size == 3;
20
+		case ATAG_VIDEOLFB:
21
+			return a->size == 8;
22
+		case ATAG_CMDLINE:
23
+			return a->size >= 3;
24
+		default:
25
+			return 0;
26
+	}
27
+}
28
+
29
+int _atags_mem_region(struct atag **mem_header, int initialize) {
30
+	if (initialize)
31
+		*mem_header = (struct atag *)atags_ptr;
32
+	else
33
+		*mem_header = (struct atag *)((uint32 *)*mem_header + (*mem_header)->size);
34
+
35
+	while (atag_valid(*mem_header) && (*mem_header)->tag != ATAG_NONE && (*mem_header)->tag != ATAG_MEM) {
36
+		*mem_header = (struct atag *)((uint32 *)*mem_header + (*mem_header)->size);
37
+	}
38
+
39
+	if (!atag_valid(*mem_header) || (*mem_header)->tag == ATAG_NONE)
40
+		return -1;
41
+	return 0;
42
+}

+ 8 - 7
kernel/mm.c

@@ -32,12 +32,13 @@ void mm_add_free_region(void *start, void *end) {
32 32
 	void *page;
33 33
 
34 34
 	//make sure both start and end address are aligned to the size of a page
35
-	if ((unsigned int)start & MM_PAGE_SIZE || (unsigned int)(end+1) & MM_PAGE_SIZE) {
36
-		print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE);
37
-		return;
38
-	}
39
-	if ((char *)end - (char *)start < MM_PAGE_SIZE<<1) {
40
-		print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE);
35
+	if ((unsigned int)start % MM_PAGE_SIZE != 0)
36
+		start = (char*)start + (MM_PAGE_SIZE - ((unsigned int)start % MM_PAGE_SIZE));
37
+	if (((unsigned int)end + 1) % MM_PAGE_SIZE != 0)
38
+		end = (char*)end - ((unsigned int)end + 1) % MM_PAGE_SIZE;
39
+
40
+	if ((char *)end + 1 - (char *)start < MM_PAGE_SIZE<<1) {
41
+		print("Error: Supplied memory area(%x,%x) is smaller than the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE);
41 42
 		return;
42 43
 	}
43 44
 
@@ -46,7 +47,7 @@ void mm_add_free_region(void *start, void *end) {
46 47
 
47 48
 	//TODO we're potentially losing memory here because we're calculating
48 49
 	//the number of page structs we need even for those pages that will contain only page structs
49
-	num_pages = ((char *)end - (char *)start) / MM_PAGE_SIZE;
50
+	num_pages = ((char *)end + 1 - (char *)start) / MM_PAGE_SIZE;
50 51
 	usable_pages = num_pages - num_pages * sizeof(struct page) / MM_PAGE_SIZE;
51 52
 	if (num_pages * sizeof(struct page) % MM_PAGE_SIZE)
52 53
 		usable_pages--;

+ 55 - 9
kernel/mmu.c

@@ -1,4 +1,6 @@
1 1
 #include <print.h>
2
+#include <types.h>
3
+#include <mm.h>
2 4
 
3 5
 #define SCTLR 15,0,1,0,0
4 6
 #define TTBR0 15,0,2,0,0
@@ -11,23 +13,29 @@
11 13
 #define cp_write(var, ...)  _cp_write(var, __VA_ARGS__)
12 14
 
13 15
 #define TT_BASE_SIZE (1<<14) /* 16k */
16
+#define TT_SECTION_SIZE (1<<20) /* 1mb */
14 17
 
15
-unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt;
18
+uint32 *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt;
19
+
20
+void print_mapping(void *addr) {
21
+	extern uint32 tt_base_virtual;
22
+	print("%x: %x\n", addr, *(uint32 *)(tt_base_virtual + (((uint32)addr)>>18)));
23
+}
16 24
 
17 25
 void mmu_reinit() {
18
-	extern unsigned int tt_base_virtual, tt_base_physical, start;
19
-	unsigned int curr_addr;
20
-	unsigned int *curr_tt_entry;
26
+	extern uint32 tt_base_virtual, tt_base_physical, start;
27
+	uint32 curr_addr;
28
+	uint32 *curr_tt_entry;
21 29
 	int virt_phys_offset;
22 30
 
23 31
 	virt_phys_offset = tt_base_virtual - tt_base_physical;
24 32
 	kernel_start_virt = &start;
25 33
 	kernel_start_phys = kernel_start_virt - virt_phys_offset/4;
26
-	kernel_end_virt = (unsigned int *)(tt_base_virtual + TT_BASE_SIZE);
27
-	kernel_end_phys = (unsigned int *)(tt_base_physical + TT_BASE_SIZE);
34
+	kernel_end_virt = (uint32 *)(tt_base_virtual + TT_BASE_SIZE);
35
+	kernel_end_phys = (uint32 *)(tt_base_physical + TT_BASE_SIZE);
28 36
 
29 37
 	//get the current translation table base address
30
-	curr_tt_entry = (unsigned int *)tt_base_virtual;
38
+	curr_tt_entry = (uint32 *)tt_base_virtual;
31 39
 
32 40
 	//do first loop iteration outside the loop, because we have to check against wrapping back around to know we're done
33 41
 	*curr_tt_entry = 0xc02; /* 0xc02 means read/write at any priviledge level, and that it's a section w/o PXN bit set */
@@ -38,9 +46,9 @@ void mmu_reinit() {
38 46
 	//memory, make sure we keep those mappings correct, and we'll actually
39 47
 	//swap the twp mappings so all of memory is addressable.
40 48
 	for (curr_addr = 0x00100000; curr_addr != 0; curr_addr += 0x00100000) {
41
-		if ((unsigned int *)curr_addr >= kernel_start_phys && (unsigned int *)curr_addr < kernel_end_phys) {
49
+		if ((uint32 *)curr_addr >= kernel_start_phys && (uint32 *)curr_addr < kernel_end_phys) {
42 50
 			*curr_tt_entry = (curr_addr + virt_phys_offset) | 0xc02;
43
-		} else if ((unsigned int *)curr_addr >= kernel_start_virt && (unsigned int *)curr_addr < kernel_end_virt) {
51
+		} else if ((uint32 *)curr_addr >= kernel_start_virt && (uint32 *)curr_addr < kernel_end_virt) {
44 52
 			*curr_tt_entry = (curr_addr - virt_phys_offset) | 0xc02;
45 53
 		} else {
46 54
 			*curr_tt_entry = curr_addr | 0xc02;
@@ -48,3 +56,41 @@ void mmu_reinit() {
48 56
 		curr_tt_entry++;
49 57
 	}
50 58
 }
59
+
60
+int mmu_region_contains(void *lower_a, void *upper_a, void *lower_b, void *upper_b) {
61
+	return lower_b >= lower_a && upper_b <= upper_a;
62
+}
63
+
64
+#define section_round_down(ptr) (((uint32)ptr) & ~(TT_SECTION_SIZE-1))
65
+#define section_round_up(ptr) (((((uint32)ptr) & ~1) + (TT_SECTION_SIZE-1) ) & ~(TT_SECTION_SIZE-1))
66
+
67
+/* Called one per physical memory region by bootup code. This function is
68
+ * responsible for only adding (via mm_add_free_region()) those parts of the
69
+ * memory region which are still available (i.e. aren't in the kernel and
70
+ * haven't been remapped anywhere else. */
71
+void declare_memory_region(void *lower, void *upper) {
72
+	void *k_section_start_phys = (void *)section_round_down(kernel_start_phys);
73
+	void *k_section_end_phys = (void *)(section_round_up(kernel_end_phys) - 1);
74
+	void *k_section_start_virt = (void *)section_round_down(kernel_start_virt);
75
+	void *k_section_end_virt = (void *)(section_round_up(kernel_end_virt) - 1);
76
+
77
+	if (upper - lower < 1) {
78
+		print("Warning: declare_memory_region() called with lower=%x, upper=%x. Ignoring.\n", lower, upper);
79
+		return;
80
+	}
81
+
82
+	//TODO It's possible (though highly unlikely) that the kernel (virtual)
83
+	//is split across two different memory regions. We should probably
84
+	//handle this.
85
+	if (mmu_region_contains(lower, upper, k_section_start_phys, k_section_end_phys)) {
86
+		//Don't map any of the physical kernel's memory
87
+		declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1));
88
+		declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper);
89
+		mm_add_free_region(kernel_end_virt, k_section_end_virt);
90
+	} else if (mmu_region_contains(lower, upper, k_section_start_virt, k_section_end_virt)) {
91
+		declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1));
92
+		declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper);
93
+	} else {
94
+		mm_add_free_region(lower, upper);
95
+	}
96
+}

+ 14 - 7
kernel/start_kernel.c

@@ -1,3 +1,4 @@
1
+#include <atags.h>
1 2
 #include <mmu.h>
2 3
 #include <mm.h>
3 4
 #include <print.h>
@@ -52,7 +53,8 @@ void test_memory() {
52 53
 }
53 54
  
54 55
 int main(void) {
55
-	char *lower;
56
+	char *lower, *upper;
57
+	struct atag *atags;
56 58
 
57 59
 	//setup MMU
58 60
 	mmu_reinit();
@@ -61,12 +63,17 @@ int main(void) {
61 63
 
62 64
 	//setup memory
63 65
 	mm_init();
64
-	mm_add_free_region((void*)0x60000000, (void*)0x7FFFFFFF);
65
-	mm_add_free_region((void*)0x80000000, (void*)0x800FFFFF);
66
-	lower = (char*) &kernel_end_virt;
67
-	if ((unsigned int)lower % MM_PAGE_SIZE != 0)
68
-		lower += (MM_PAGE_SIZE - ((unsigned int)lower % MM_PAGE_SIZE));
69
-	mm_add_free_region((void*)lower, (void*)0x9FFFFFFF); //subtract the memory used by the kernel
66
+
67
+	if (atags_first_mem_region(&atags)) {
68
+		print("Error: atags must contain at least one memory region\n");
69
+		return -1;
70
+	}
71
+
72
+	do {
73
+		lower = (char *)atags->data.mem.start;
74
+		upper = lower + atags->data.mem.size - 1;
75
+		declare_memory_region(lower, upper);
76
+	} while (!atags_next_mem_region(&atags));
70 77
 
71 78
 	test_memory();
72 79