summaryrefslogtreecommitdiff
path: root/kernel/kmalloc.c
blob: 38db6fc93f1ea70a6b6283fd3588634bc2d2f9f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include "kernel.h"
#include "kmalloc.h"
#include "spinlock.h"
#include "log.h"

#define BLOCKS (KMALLOC_MEM_SIZE/KMALLOC_BLOCK_SIZE)

// this is in .bss so we can assume it was zeroed!
static uint8_t    data[KMALLOC_MEM_SIZE] __attribute__((aligned (4096))); // bytes
static uint8_t    map[BLOCKS];
//
static uint32_t   data_addr;

static uint32_t next_free(uint32_t start)
{
    for(int i=start;i<BLOCKS;i++)
    {
	if(!map[i])return i;
	i+=map[i]-1;
    }
    return BLOCKS; // out of mem
}

static uint32_t next_used(uint32_t start,uint32_t max)
{
    for(int i=start;i<BLOCKS;i++)
    {
	if(map[i])return i; 
	//means i is free
	if(i-start>=max)return i;
    }
    return BLOCKS; // all free
}

static uint32_t free_cont(uint32_t blocks)
{
    uint32_t pos=0;

    while(1)
    {
	pos=next_free(pos);
//	klog("next_free:%d",pos);
	if(pos+blocks>=BLOCKS)return BLOCKS; // out of mem
	uint32_t end=next_used(pos,blocks);
//	klog("next_used:%d",end);
	if(end-pos>=blocks)return pos;
//	klog("here we have only %d blocks but we need at least %d",end-pos+1,blocks);
	pos=end;
    }
}

static void mark_used(uint32_t start,uint32_t blocks)
{
    uint32_t b=blocks;
    for(int i=start;i<start+blocks;i++)
    {
	if(map[i]!=0)kpanic("memory map corrupted?");
	map[i]=b;
	b--;
    }
}

static void mark_free(uint32_t start,uint32_t blocks)
{
    if(map[start]!=blocks)
    {
	kpanic("mark_free(%d,%d),mem map corrupted at %d (value=%d)?",start,blocks,start,map[start]);
    }

    if(start!=0&&(map[start-1]!=1&&map[start-1]!=0))
    {
	kpanic("mem map corrupted one before %d (value=%d)?",start,map[start-1]);
    }

    map[start]=0;
    if(blocks>1)mark_free(start+1,blocks-1);
}

// will be initialized on first call to kballoc() //
static void kmallocinit()
{
    data_addr=data;
    if(data_addr%4096)kpanic("kmalloc data not aligned properly.");

    klog("In-Kernel Block Memory Allocation Initialized at: 0x%08X (free: %d x 4096KB BLOCKS)",data_addr,BLOCKS);
}

// kernel block memory allocation //
uint32_t kballoc(uint32_t size)
{
    static bool init=false;
    if(size>255)kpanic("max supported size 255 blocks");
    
    spinlock_spin(SPINLOCK_ALLOC);
    if(!init){kmallocinit(); init=true;}

    uint32_t blk=free_cont(size);
    if(blk==BLOCKS)kpanic("out of mem");
    mark_used(blk,size);
    spinlock_release(SPINLOCK_ALLOC);

    klog("allocated %d blocks at 0x%08X",size,data_addr+blk*4096);
    return data_addr+blk*4096;
}

void kbfree(uint32_t pos)
{
    uint32_t blk=(pos-data_addr)/4096;
    spinlock_spin(SPINLOCK_ALLOC);
    klog("freeing %d blocks at 0x%08X",map[blk],pos);
    mark_free(blk,map[blk]);
    spinlock_release(SPINLOCK_ALLOC);
}

void kmalloc_sysfs(ringbuffer *r,void (*f)(ringbuffer *r,char *fmt, ...))
{
    uint32_t free=0;
    uint32_t used=0;

    char cmap[BLOCKS];
    cmap[200]=0;

    for(int i=0;i<BLOCKS;i++)
    {
	if(map[i])
        {
            cmap[i]='X';
            used++;
        }
	else
        {
            cmap[i]='_';
            free++;
        }
    }

    f(r,"kernel blocks allocation/deallocation");
    f(r,"total 4096kb blocks: %d (%d bytes)",BLOCKS,BLOCKS*4096);
    f(r,"used 4096kb blocks: %d (%d bytes)",used,used*4096);
    f(r,"free 4096kb blocks: %d (%d bytes)",free,free*4096);
//    f(r,"%s",cmap); // TODO: watch out for buffer overflow in f

}