732 lines
21 KiB
C
732 lines
21 KiB
C
#include "libc.h"
|
|
#include "list.h"
|
|
#include "minmax.h"
|
|
#include "swintr.h"
|
|
#include "syscall.h"
|
|
|
|
int memcmp(const void *aptr, const void *bptr, size_t size)
|
|
{
|
|
const unsigned char *a = (const unsigned char *)aptr;
|
|
const unsigned char *b = (const unsigned char *)bptr;
|
|
for (size_t i = 0; i < size; i++) {
|
|
if (a[i] < b[i])
|
|
return -1;
|
|
else if (b[i] < a[i])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Inspirated by https://interrupt.memfault.com/blog/memcpy-newlib-nano
|
|
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
|
#define UNALIGNED(X, Y) \
|
|
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
|
|
|
/* How many bytes are copied each iteration of the 4X unrolled loop. */
|
|
#define BIGBLOCKSIZE (sizeof (long) << 2)
|
|
|
|
/* How many bytes are copied each iteration of the word copy loop. */
|
|
#define LITTLEBLOCKSIZE (sizeof (long))
|
|
|
|
/* Threshhold for punting to the byte copier. */
|
|
#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE)
|
|
|
|
void *memcpy(void *dst0, const void *src0, size_t len0)
|
|
{
|
|
#if 0
|
|
char *dstChar = dst0;
|
|
const char *srcChar = src0;
|
|
for (size_t i = 0; i < len0; i++) {
|
|
*(dstChar++) = *(srcChar++);
|
|
}
|
|
return dst0;
|
|
#else
|
|
char *dst = dst0;
|
|
const char *src = src0;
|
|
|
|
/* If the size is small, or either SRC or DST is unaligned,
|
|
then punt into the byte copy loop. This should be rare. */
|
|
if (!TOO_SMALL(len0) && !UNALIGNED(src, dst)) {
|
|
long *aligned_dst;
|
|
const long *aligned_src;
|
|
|
|
aligned_dst = (long *)dst;
|
|
aligned_src = (long *)src;
|
|
|
|
/* Copy 4X long words at a time if possible. */
|
|
while (len0 >= BIGBLOCKSIZE) {
|
|
*aligned_dst++ = *aligned_src++;
|
|
*aligned_dst++ = *aligned_src++;
|
|
*aligned_dst++ = *aligned_src++;
|
|
*aligned_dst++ = *aligned_src++;
|
|
len0 -= BIGBLOCKSIZE;
|
|
}
|
|
|
|
/* Copy one long word at a time if possible. */
|
|
while (len0 >= LITTLEBLOCKSIZE) {
|
|
*aligned_dst++ = *aligned_src++;
|
|
len0 -= LITTLEBLOCKSIZE;
|
|
}
|
|
|
|
/* Pick up any residual with a byte copier. */
|
|
dst = (char *)aligned_dst;
|
|
src = (char *)aligned_src;
|
|
}
|
|
|
|
while (len0--)
|
|
*dst++ = *src++;
|
|
|
|
return dst0;
|
|
#endif
|
|
}
|
|
|
|
void *memmove(void *dst, const void *src, size_t n)
|
|
{
|
|
char *dstChar = dst;
|
|
const char *srcChar = src;
|
|
for (size_t i = 0; i < n; i++) {
|
|
*(dstChar++) = *(srcChar++);
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
void *memset(void *src, int c, size_t n)
|
|
{
|
|
for (char *ptr = (char *)src; n > 0; n--, ptr++) {
|
|
*ptr = (char)c;
|
|
}
|
|
return src;
|
|
}
|
|
|
|
char *itoa(long long int value, char *str, int base)
|
|
{
|
|
char *rc;
|
|
char *ptr;
|
|
char *low;
|
|
// Check for supported base.
|
|
if (base < 2 || base > 36) {
|
|
*str = '\0';
|
|
return str;
|
|
}
|
|
rc = ptr = str;
|
|
// Set '-' for negative decimals.
|
|
if (value < 0 && base == 10) {
|
|
*ptr++ = '-';
|
|
}
|
|
// Remember where the numbers start.
|
|
low = ptr;
|
|
// The actual conversion.
|
|
do {
|
|
// Modulo is negative for negative value. This trick makes abs() unnecessary.
|
|
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"
|
|
[35 + value % base];
|
|
value /= base;
|
|
} while (value);
|
|
// Terminating the string.
|
|
*ptr-- = '\0';
|
|
// Invert the numbers.
|
|
while (low < ptr) {
|
|
char tmp = *low;
|
|
*low++ = *ptr;
|
|
*ptr-- = tmp;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* K&R */
|
|
void reverse(char s[])
|
|
{
|
|
int c, i, j;
|
|
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
|
|
c = s[i];
|
|
s[i] = s[j];
|
|
s[j] = c;
|
|
}
|
|
}
|
|
|
|
/* K&R */
|
|
int strlen(const char s[])
|
|
{
|
|
int i = 0;
|
|
while (s[i] != '\0')
|
|
++i;
|
|
return i;
|
|
}
|
|
|
|
/* K&R
|
|
* Returns <0 if s1<s2, 0 if s1==s2, >0 if s1>s2 */
|
|
int strcmp(const char s1[], const char s2[])
|
|
{
|
|
int i;
|
|
for (i = 0; s1[i] == s2[i]; i++) {
|
|
if (s1[i] == '\0')
|
|
return 0;
|
|
}
|
|
return s1[i] - s2[i];
|
|
}
|
|
|
|
unsigned int strnlen(const char *s, size_t count)
|
|
{
|
|
const char *sc;
|
|
|
|
for (sc = s; count-- && *sc != '\0'; ++sc)
|
|
/* nothing */ continue;
|
|
|
|
return sc - s;
|
|
}
|
|
|
|
char *strzcpy(register char *dst, register const char *src, register int len)
|
|
{
|
|
int i;
|
|
|
|
if (len <= 0)
|
|
return dst;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
dst[i] = src[i];
|
|
if (src[i] == '\0')
|
|
return dst;
|
|
}
|
|
|
|
dst[len - 1] = '\0';
|
|
return dst;
|
|
}
|
|
|
|
int printf(const char *format, ...)
|
|
{
|
|
int ret;
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
ret = vprintf(format, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int puts(const char *str)
|
|
{
|
|
int ret = 0;
|
|
while (*str) {
|
|
putc(*(str++));
|
|
ret++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3)
|
|
// = 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3)
|
|
#define PRINT_INT(name, type) \
|
|
int print##name(type integer, char *str, size_t size) \
|
|
{ \
|
|
char num[sizeof(integer) * 3]; \
|
|
int i = 0; \
|
|
int c = 0; \
|
|
int ret = 0; \
|
|
\
|
|
if (integer < 0) { \
|
|
if (str) { \
|
|
if (size) { \
|
|
str[c++] = '-'; \
|
|
size--; \
|
|
ret++; \
|
|
} else { \
|
|
return ret; \
|
|
} \
|
|
} else { \
|
|
ret++; \
|
|
} \
|
|
} \
|
|
\
|
|
do { \
|
|
int digit = integer % 10; \
|
|
num[i++] = (digit > 0) ? digit : -digit; \
|
|
integer = integer / 10; \
|
|
} while (integer != 0); \
|
|
\
|
|
for (i = i - 1; i >= 0; i--) { \
|
|
if (str) { \
|
|
if (size) { \
|
|
str[c++] = num[i] + '0'; \
|
|
size--; \
|
|
ret++; \
|
|
} else { \
|
|
return ret; \
|
|
} \
|
|
} else { \
|
|
ret++; \
|
|
} \
|
|
} \
|
|
return ret; \
|
|
}
|
|
|
|
PRINT_INT(Int, int);
|
|
PRINT_INT(Int64, long long int);
|
|
|
|
int vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
int c = 0;
|
|
|
|
while (format[i] != '\0' && (size || !str)) {
|
|
switch (format[i]) {
|
|
case '%':
|
|
switch (format[i + 1]) {
|
|
case 'i':
|
|
case 'd': {
|
|
int s;
|
|
int d = va_arg(ap, int);
|
|
if (str)
|
|
s = printInt(d, &str[c], size);
|
|
else
|
|
s = printInt(d, NULL, size);
|
|
|
|
size -= s;
|
|
c += s;
|
|
ret += s;
|
|
break;
|
|
}
|
|
case 'p':
|
|
case 'x': {
|
|
char val[sizeof(int) * 2];
|
|
unsigned int valIdx = 0;
|
|
int d = va_arg(ap, int);
|
|
itoa(d, val, 16);
|
|
if (str) {
|
|
while (val[valIdx]) {
|
|
if (size) {
|
|
str[c++] = val[valIdx++];
|
|
size--;
|
|
ret++;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
} else {
|
|
ret += strlen(val);
|
|
}
|
|
break;
|
|
}
|
|
case 'c': {
|
|
if (str) {
|
|
int ch = va_arg(ap, int);
|
|
str[c++] = ch;
|
|
size--;
|
|
}
|
|
ret++;
|
|
break;
|
|
}
|
|
case 's': {
|
|
char *stri = va_arg(ap, char *);
|
|
if (!stri)
|
|
stri = "[NULL STR]";
|
|
if (str) {
|
|
while (*stri) {
|
|
if (size) {
|
|
str[c++] = *(stri++);
|
|
size--;
|
|
ret++;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
} else {
|
|
ret += strlen(stri);
|
|
}
|
|
break;
|
|
}
|
|
case '%':
|
|
if (str) {
|
|
str[c++] = '%';
|
|
size--;
|
|
}
|
|
ret++;
|
|
break;
|
|
case 'l':
|
|
switch (format[i + 2]) {
|
|
case 'l':
|
|
switch (format[i + 3]) {
|
|
case 'd': {
|
|
int s;
|
|
long long int d = va_arg(ap, long long int);
|
|
if (str)
|
|
s = printInt64(d, &str[c], size);
|
|
else
|
|
s = printInt64(d, NULL, size);
|
|
|
|
size -= s;
|
|
c += s;
|
|
ret += s;
|
|
break;
|
|
}
|
|
case 'p':
|
|
case 'x': {
|
|
char val[sizeof(long long int) * 2];
|
|
unsigned int valIdx = 0;
|
|
long long int d = va_arg(ap, long long int);
|
|
itoa(d, val, 16);
|
|
if (str) {
|
|
while (val[valIdx]) {
|
|
if (size) {
|
|
str[c++] = val[valIdx++];
|
|
size--;
|
|
ret++;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
} else {
|
|
ret += strlen(val);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
break;
|
|
case 'i':
|
|
case 'd': {
|
|
int s;
|
|
long int d = va_arg(ap, long int);
|
|
if (str)
|
|
s = printInt64(d, &str[c], size);
|
|
else
|
|
s = printInt64(d, NULL, size);
|
|
|
|
size -= s;
|
|
c += s;
|
|
ret += s;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
break;
|
|
}
|
|
i++;
|
|
break;
|
|
|
|
default:
|
|
if (str) {
|
|
str[c++] = format[i];
|
|
size--;
|
|
}
|
|
ret++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (str) {
|
|
if (size) {
|
|
str[c++] = '\0';
|
|
} else {
|
|
if (c > 0) {
|
|
str[c - 1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int vprintf(const char *fmt, va_list ap)
|
|
{
|
|
char tmp[256];
|
|
int idx = 0;
|
|
int ret = vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
|
while (ret > 0 && tmp[idx]) {
|
|
putc(tmp[idx++]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int asprintf(char **strp, const char *fmt, ...)
|
|
{
|
|
int ret;
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
ret = vasprintf(strp, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int vasprintf(char **strp, const char *fmt, va_list ap)
|
|
{
|
|
int n = 0;
|
|
size_t size = 0;
|
|
char *p = malloc(256);
|
|
|
|
/* Determine required size */
|
|
|
|
n = vsnprintf(p, size, fmt, ap);
|
|
|
|
if (n < 0){
|
|
free(p);
|
|
return -1;
|
|
}
|
|
|
|
/* One extra byte for '\0' */
|
|
|
|
size = min(256U, (size_t)n + 1);
|
|
|
|
n = vsnprintf(p, size, fmt, ap);
|
|
|
|
if (n < 0) {
|
|
free(p);
|
|
return -1;
|
|
}
|
|
*strp = p;
|
|
|
|
return size;
|
|
}
|
|
|
|
int syscall5(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3,
|
|
unsigned int arg4, unsigned int arg5)
|
|
{
|
|
unsigned int args[] = {arg3, arg4, arg5};
|
|
return syscall3(id, arg1, arg2, (unsigned)args);
|
|
}
|
|
|
|
int syscall4(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3,
|
|
unsigned int arg4)
|
|
{
|
|
unsigned int args[] = {arg3, arg4};
|
|
return syscall3(id, arg1, arg2, (unsigned)args);
|
|
}
|
|
|
|
int syscall3(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3)
|
|
{
|
|
int ret;
|
|
|
|
asm volatile("movl %1,%%eax \n"
|
|
"movl %2,%%ebx \n"
|
|
"movl %3,%%ecx \n"
|
|
"movl %4,%%edx \n"
|
|
"int %5\n"
|
|
"movl %%eax, %0"
|
|
: "=g"(ret)
|
|
: "g"(id), "g"(arg1), "g"(arg2), "g"(arg3), "i"(SYSCALL_INTR_NB)
|
|
: "eax", "ebx", "ecx", "edx");
|
|
|
|
return ret;
|
|
}
|
|
|
|
int syscall2(int id, unsigned int arg1, unsigned int arg2)
|
|
{
|
|
return syscall3(id, arg1, arg2, 0);
|
|
}
|
|
|
|
int syscall1(int id, unsigned int arg1)
|
|
{
|
|
return syscall3(id, arg1, 0, 0);
|
|
}
|
|
|
|
int syscall0(int id)
|
|
{
|
|
return syscall3(id, 0, 0, 0);
|
|
}
|
|
|
|
void _exit(int status)
|
|
{
|
|
syscall1(SYSCALL_ID_EXIT, status);
|
|
}
|
|
|
|
int putc(const int c){
|
|
return syscall1(SYSCALL_ID_PUTC, c);
|
|
}
|
|
|
|
void helo()
|
|
{
|
|
syscall0(SYSCALL_ID_HELO);
|
|
}
|
|
|
|
int testSycall5(uint arg1, uint arg2, uint arg3, uint arg4, uint arg5)
|
|
{
|
|
return syscall5(SYSCALL_ID_TEST, arg1, arg2, arg3, arg4, arg5);
|
|
}
|
|
|
|
char readc()
|
|
{
|
|
return syscall0(SYSCALL_ID_READ);
|
|
}
|
|
|
|
char getchar()
|
|
{
|
|
char c = 0;
|
|
do {
|
|
c = readc();
|
|
} while (c == 0);
|
|
return c;
|
|
}
|
|
|
|
int readline(char *buf, int size)
|
|
{
|
|
int i = 0;
|
|
for (; i < size - 1; i++) {
|
|
char key = getchar();
|
|
|
|
if (key == '\n')
|
|
break;
|
|
|
|
if(key == '\b' && i>=1){
|
|
buf[i-1] = '\0';
|
|
i-=2;
|
|
continue;
|
|
}
|
|
|
|
buf[i] = key;
|
|
}
|
|
buf[i] = '\0';
|
|
return i == (size-1);
|
|
}
|
|
|
|
int brk(void *addr)
|
|
{
|
|
uintptr_t new = syscall1(SYSCALL_ID_BRK, (unsigned int)addr);
|
|
|
|
//errno = ENOMEM
|
|
return (new >= (uintptr_t)addr)?0:-1;
|
|
|
|
}
|
|
|
|
void *sbrk(intptr_t increment)
|
|
{
|
|
void *current = (void *)syscall1(SYSCALL_ID_BRK, 0);
|
|
if (increment == 0) {
|
|
return current;
|
|
}
|
|
if ((uintptr_t)syscall1(SYSCALL_ID_BRK, (uintptr_t)current + increment) < ((uintptr_t)current + increment)) {
|
|
// errno = ENOMEM
|
|
return (void *)-1;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
// Malloc internal
|
|
struct heapBlock {
|
|
size_t size;
|
|
struct heapBlock *next, *prev;
|
|
int free;
|
|
};
|
|
|
|
static struct heapBlock *heapBlkList = NULL;
|
|
|
|
static struct heapBlock *findFreeBlock(size_t size)
|
|
{
|
|
struct heapBlock *cur = NULL;
|
|
struct heapBlock *found = NULL;
|
|
int idx;
|
|
|
|
list_foreach(heapBlkList, cur, idx)
|
|
{
|
|
if (cur->size >= size && cur->free) {
|
|
found = cur;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static struct heapBlock *allocNewBlock(size_t size)
|
|
{
|
|
struct heapBlock *blk = sbrk(size + sizeof(struct heapBlock));
|
|
struct heapBlock *head = sbrk(0);
|
|
size_t blkSize = (intptr_t)head - (intptr_t)blk - sizeof(struct heapBlock);
|
|
if (blk == (void *)-1) {
|
|
return NULL;
|
|
}
|
|
blk->size = blkSize;
|
|
blk->free = 1;
|
|
list_add_tail(heapBlkList, blk);
|
|
|
|
return blk;
|
|
}
|
|
|
|
static struct heapBlock *splitBlock(struct heapBlock *blk, size_t neededSize)
|
|
{
|
|
if (blk->size < neededSize + sizeof(struct heapBlock) + 1) {
|
|
return NULL;
|
|
}
|
|
struct heapBlock *newBlk = (struct heapBlock *)((uintptr_t)(blk + 1) + neededSize);
|
|
newBlk->free = 1;
|
|
newBlk->size = blk->size - sizeof(struct heapBlock) - neededSize;
|
|
blk->size = neededSize;
|
|
|
|
return newBlk;
|
|
}
|
|
|
|
void *malloc(size_t size)
|
|
{
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
struct heapBlock *blk = findFreeBlock(size);
|
|
if (!blk)
|
|
blk = allocNewBlock(size);
|
|
if (!blk)
|
|
return NULL;
|
|
struct heapBlock *remainBlock = splitBlock(blk, size);
|
|
if (remainBlock) {
|
|
list_add_head(heapBlkList, remainBlock);
|
|
}
|
|
blk->free = 0;
|
|
|
|
return blk + 1; // return the area after the blk description
|
|
}
|
|
|
|
static struct heapBlock *getHeapBlock(void *ptr)
|
|
{
|
|
return (struct heapBlock *)ptr - 1;
|
|
}
|
|
|
|
void free(void *ptr)
|
|
{
|
|
if (!ptr)
|
|
return;
|
|
|
|
struct heapBlock *blk = getHeapBlock(ptr);
|
|
|
|
assert(blk->free == 0);
|
|
blk->free = 1;
|
|
}
|
|
|
|
void *calloc(size_t nmemb, size_t size)
|
|
{
|
|
size_t allocSize = nmemb * size;
|
|
void *ptr = malloc(allocSize);
|
|
|
|
if (ptr != NULL)
|
|
memset(ptr, 0, allocSize);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *realloc(void *ptr, size_t size)
|
|
{
|
|
if (!ptr) {
|
|
return malloc(size);
|
|
}
|
|
|
|
struct heapBlock *blk = getHeapBlock(ptr);
|
|
if (blk->size >= size) {
|
|
return ptr;
|
|
}
|
|
|
|
void *new_ptr;
|
|
new_ptr = malloc(size);
|
|
if (!new_ptr) {
|
|
return NULL;
|
|
}
|
|
memmove(new_ptr, ptr, blk->size);
|
|
free(ptr);
|
|
|
|
return new_ptr;
|
|
}
|
|
|
|
void *mmap(void *addr, size_t len, int prot, int flags, char *path){
|
|
|
|
return (void *)syscall5(SYSCALL_ID_MMAP, (unsigned int)addr, len, prot, flags, (unsigned int)path);
|
|
}
|