2011-12-10 17:34:10 -06:00
|
|
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
|
|
|
*
|
|
|
|
* System Functions
|
|
|
|
*
|
|
|
|
* Part of the ToAru OS Kernel
|
|
|
|
* (C) 2011 Kevin Lange
|
|
|
|
* Released under the terms of the NCSA License, see the included
|
|
|
|
* README file for further information.
|
2011-03-28 19:34:44 -05:00
|
|
|
*/
|
2011-01-28 13:56:56 -06:00
|
|
|
#include <system.h>
|
|
|
|
|
2011-12-10 19:15:12 -06:00
|
|
|
|
|
|
|
void spin_lock(uint8_t volatile * lock) {
|
|
|
|
while(__sync_lock_test_and_set(lock, 0x01)) {
|
|
|
|
switch_task();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void spin_unlock(uint8_t volatile * lock) {
|
|
|
|
__sync_lock_release(lock);
|
|
|
|
}
|
|
|
|
|
2011-01-28 13:56:56 -06:00
|
|
|
/*
|
|
|
|
* memcpy
|
|
|
|
* Copy from source to destination. Assumes that
|
|
|
|
* source and destination are not overlapping.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
memcpy(
|
|
|
|
void * restrict dest,
|
|
|
|
const void * restrict src,
|
|
|
|
size_t count
|
|
|
|
) {
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("cld; rep movsb" : "+c" (count), "+S" (src), "+D" (dest) :: "memory");
|
2011-01-28 13:56:56 -06:00
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2011-03-29 16:35:02 -05:00
|
|
|
int
|
|
|
|
max(int a, int b) {
|
|
|
|
return (a > b) ? a : b;
|
|
|
|
}
|
|
|
|
|
2011-12-26 19:23:58 -06:00
|
|
|
int
|
|
|
|
min(int a, int b) {
|
|
|
|
return (a > b) ? b : a;
|
|
|
|
}
|
|
|
|
|
2011-03-29 16:35:02 -05:00
|
|
|
int
|
|
|
|
abs(int a) {
|
|
|
|
return (a >= 0) ? a : -a;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
swap(int *a, int *b) {
|
|
|
|
int t = *a;
|
|
|
|
*a = *b;
|
|
|
|
*b = t;
|
|
|
|
}
|
|
|
|
|
2011-03-28 19:34:44 -05:00
|
|
|
void *
|
|
|
|
memmove(
|
|
|
|
void * restrict dest,
|
|
|
|
const void * restrict src,
|
|
|
|
size_t count
|
|
|
|
) {
|
|
|
|
size_t i;
|
|
|
|
unsigned char *a = dest;
|
|
|
|
const unsigned char *b = src;
|
|
|
|
if (src < dest) {
|
|
|
|
for ( i = count; i > 0; --i) {
|
|
|
|
a[i-1] = b[i-1];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for ( i = 0; i < count; ++i) {
|
|
|
|
a[i] = b[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2011-01-29 14:12:00 -06:00
|
|
|
int
|
|
|
|
strcmp(
|
|
|
|
const char * a,
|
|
|
|
const char * b
|
|
|
|
) {
|
|
|
|
uint32_t i = 0;
|
|
|
|
while (1) {
|
|
|
|
if (a[i] < b[i]) {
|
|
|
|
return -1;
|
|
|
|
} else if (a[i] > b[i]) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
if (a[i] == '\0') {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-28 13:56:56 -06:00
|
|
|
/*
|
|
|
|
* memset
|
|
|
|
* Set `count` bytes to `val`.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
memset(
|
|
|
|
void *b,
|
|
|
|
int val,
|
|
|
|
size_t count
|
|
|
|
) {
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("cld; rep stosb" : "+c" (count), "+D" (b) : "a" (val) : "memory");
|
2011-01-28 13:56:56 -06:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* memsetw
|
|
|
|
* Set `count` shorts to `val`.
|
|
|
|
*/
|
|
|
|
unsigned short *
|
|
|
|
memsetw(
|
|
|
|
unsigned short *dest,
|
|
|
|
unsigned short val,
|
|
|
|
int count
|
|
|
|
) {
|
|
|
|
int i;
|
|
|
|
i = 0;
|
|
|
|
for ( ; i < count; ++i ) {
|
|
|
|
dest[i] = val;
|
|
|
|
}
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* strlen
|
|
|
|
* Returns the length of a given `str`.
|
|
|
|
*/
|
2011-11-17 15:55:59 -06:00
|
|
|
uint32_t
|
2011-01-28 13:56:56 -06:00
|
|
|
strlen(
|
|
|
|
const char *str
|
|
|
|
) {
|
|
|
|
int i = 0;
|
|
|
|
while (str[i] != (char)0) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2011-03-25 22:29:54 -05:00
|
|
|
uint32_t __attribute__ ((pure)) krand() {
|
|
|
|
static uint32_t x = 123456789;
|
|
|
|
static uint32_t y = 362436069;
|
|
|
|
static uint32_t z = 521288629;
|
|
|
|
static uint32_t w = 88675123;
|
|
|
|
|
|
|
|
uint32_t t;
|
|
|
|
|
|
|
|
t = x ^ (x << 11);
|
|
|
|
x = y; y = z; z = w;
|
|
|
|
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
|
|
|
|
}
|
|
|
|
|
2011-02-10 21:45:29 -06:00
|
|
|
/*
|
|
|
|
* atoi
|
|
|
|
* Naïve implementation thereof.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
atoi(
|
|
|
|
const char *str
|
|
|
|
) {
|
|
|
|
uint32_t len = strlen(str);
|
|
|
|
uint32_t out = 0;
|
|
|
|
uint32_t i;
|
|
|
|
uint32_t pow = 1;
|
|
|
|
for (i = len; i > 0; --i) {
|
|
|
|
out += (str[i-1] - 48) * pow;
|
|
|
|
pow *= 10;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2011-03-25 22:29:54 -05:00
|
|
|
unsigned short
|
|
|
|
inports(
|
|
|
|
unsigned short _port
|
|
|
|
) {
|
|
|
|
unsigned short rv;
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("inw %1, %0" : "=a" (rv) : "dN" (_port));
|
2011-03-25 22:29:54 -05:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
outports(
|
|
|
|
unsigned short _port,
|
|
|
|
unsigned short _data
|
|
|
|
) {
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("outw %1, %0" : : "dN" (_port), "a" (_data));
|
2011-03-25 22:29:54 -05:00
|
|
|
}
|
|
|
|
|
2011-03-28 19:34:44 -05:00
|
|
|
unsigned int
|
|
|
|
inportl(
|
|
|
|
unsigned short _port
|
|
|
|
) {
|
|
|
|
unsigned short rv;
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("inl %%dx, %%eax" : "=a" (rv) : "dN" (_port));
|
2011-03-28 19:34:44 -05:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
outportl(
|
|
|
|
unsigned short _port,
|
|
|
|
unsigned int _data
|
|
|
|
) {
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("outl %%eax, %%dx" : : "dN" (_port), "a" (_data));
|
2011-03-28 19:34:44 -05:00
|
|
|
}
|
|
|
|
|
2011-03-25 22:29:54 -05:00
|
|
|
|
2011-01-28 13:56:56 -06:00
|
|
|
/*
|
|
|
|
* inportb
|
|
|
|
* Read from an I/O port.
|
|
|
|
*/
|
|
|
|
unsigned char
|
|
|
|
inportb(
|
|
|
|
unsigned short _port
|
|
|
|
) {
|
|
|
|
unsigned char rv;
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("inb %1, %0" : "=a" (rv) : "dN" (_port));
|
2011-01-28 13:56:56 -06:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* outportb
|
|
|
|
* Write to an I/O port.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
outportb(
|
|
|
|
unsigned short _port,
|
|
|
|
unsigned char _data
|
|
|
|
) {
|
2011-10-31 01:17:26 -05:00
|
|
|
asm volatile ("outb %1, %0" : : "dN" (_port), "a" (_data));
|
2011-01-28 13:56:56 -06:00
|
|
|
}
|
|
|
|
|
2011-12-13 00:48:04 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Output multiple sets of shorts
|
|
|
|
*/
|
|
|
|
void outportsm(
|
|
|
|
unsigned short port,
|
|
|
|
unsigned char * data,
|
|
|
|
unsigned long size
|
|
|
|
) {
|
|
|
|
asm volatile ("rep outsw" : "+S" (data), "+c" (size) : "d" (port));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Input multiple sets of shorts
|
|
|
|
*/
|
|
|
|
void inportsm(
|
|
|
|
unsigned short port,
|
|
|
|
unsigned char * data,
|
|
|
|
unsigned long size
|
|
|
|
) {
|
|
|
|
asm volatile ("rep insw" : "+D" (data), "+c" (size) : "d" (port) : "memory");
|
|
|
|
}
|
|
|
|
|
2011-02-07 14:30:17 -06:00
|
|
|
char *
|
|
|
|
strtok_r(
|
|
|
|
char * str,
|
|
|
|
const char * delim,
|
|
|
|
char ** saveptr
|
|
|
|
) {
|
|
|
|
char * token;
|
|
|
|
if (str == NULL) {
|
|
|
|
str = *saveptr;
|
|
|
|
}
|
|
|
|
str += strspn(str, delim);
|
|
|
|
if (*str == '\0') {
|
|
|
|
*saveptr = str;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
token = str;
|
|
|
|
str = strpbrk(token, delim);
|
|
|
|
if (str == NULL) {
|
|
|
|
*saveptr = (char *)lfind(token, '\0');
|
|
|
|
} else {
|
|
|
|
*str = '\0';
|
|
|
|
*saveptr = str + 1;
|
|
|
|
}
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
lfind(
|
|
|
|
const char * str,
|
|
|
|
const char accept
|
|
|
|
) {
|
|
|
|
size_t i = 0;
|
|
|
|
while ( str[i] != accept) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return (size_t)(str) + i;
|
|
|
|
}
|
|
|
|
|
2011-11-01 18:51:15 -05:00
|
|
|
size_t
|
|
|
|
rfind(
|
|
|
|
const char * str,
|
|
|
|
const char accept
|
|
|
|
) {
|
|
|
|
size_t i = strlen(str) - 1;
|
|
|
|
while (str[i] != accept) {
|
|
|
|
if (i == 0) return UINT32_MAX;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
return (size_t)(str) + i;
|
|
|
|
}
|
|
|
|
|
2011-11-26 15:25:59 -06:00
|
|
|
char *
|
|
|
|
strstr(const char * haystack, const char * needle) {
|
|
|
|
const char * out = NULL;
|
|
|
|
const char * ptr;
|
|
|
|
const char * acc;
|
|
|
|
const char * p;
|
|
|
|
size_t s = strlen(needle);
|
|
|
|
for (ptr = haystack; *ptr != '\0'; ++ptr) {
|
|
|
|
size_t accept = 0;
|
|
|
|
out = ptr;
|
|
|
|
p = ptr;
|
|
|
|
for (acc = needle; (*acc != '\0') && (*p != '\0'); ++acc) {
|
|
|
|
if (*p == *acc) {
|
|
|
|
accept++;
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (accept == s) {
|
|
|
|
return (char *)out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t startswith(const char * str, const char * accept) {
|
|
|
|
size_t s = strlen(accept);
|
|
|
|
for (size_t i = 0; i < s; ++i) {
|
|
|
|
if (*str != *accept) return 0;
|
|
|
|
str++;
|
|
|
|
accept++;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-02-07 14:30:17 -06:00
|
|
|
size_t
|
|
|
|
strspn(
|
|
|
|
const char * str,
|
|
|
|
const char * accept
|
|
|
|
) {
|
|
|
|
const char * ptr;
|
|
|
|
const char * acc;
|
|
|
|
size_t size = 0;
|
|
|
|
for (ptr = str; *ptr != '\0'; ++ptr) {
|
|
|
|
for (acc = accept; *acc != '\0'; ++acc) {
|
|
|
|
if (*ptr == *acc) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*acc == '\0') {
|
|
|
|
return size;
|
|
|
|
} else {
|
|
|
|
++size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
strpbrk(
|
|
|
|
const char * str,
|
|
|
|
const char * accept
|
|
|
|
) {
|
|
|
|
while (*str != '\0') {
|
|
|
|
const char *acc = accept;
|
|
|
|
while (*acc != '\0') {
|
|
|
|
if (*acc++ == *str) {
|
|
|
|
return (char *) str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++str;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
VESA mode switching support.
BIOS execution is provided through the `v8086` module, which provides
software emulation of an 8086 processor. It is not currently working
with some BIOSes and may (read: probably will be) replaced with another
emulator (x86emu comes to mind) at some point in the near future. In the
meantime, the default video mode for QEMU works with this and it's
enough to get us on real VESA instead of fake VBE. The `bochs` module
will be renamed in a future commit. Userspace programs have been
adjusted to work at bitrates other than 32 *POORLY*. If you write pixels
left-to-right, they should work fine. They only work with 24-bpp
otherwise, and then you need to be careful of what pixels you are
writing when, or you will overwrite things in other pixels.
You may pass a commandline argument like the following to set display
modes:
vid=vesa,1024,768
Or for stranger modes under QEMU or Bochs, use the bochs VBE
initializer:
vid=bochs,1280,720
Note that the address of the linear framebuffer is still found via
hackish probing instead of PCI or trusting the VBE information, so if
you have things in the wrong memory ranges (0xE0000000+), be prepared to
have them get read.
Once again, this entire commit is a massive hack. I am happy that it
worked, and I will continue to make it less hacky, but in the meantime,
this is what we've got.
Happy holidays.
2011-12-25 00:40:40 -06:00
|
|
|
|
|
|
|
void
|
|
|
|
real_mode_int(int num, struct regs * r) {
|
|
|
|
/* Give non-kernel access to the first 1MB of RAM */
|
|
|
|
for (uintptr_t i = 0; i < 0x00100000; i += 0x1000) {
|
|
|
|
alloc_frame(get_page(i, 1, kernel_directory), 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Reset protection on first 1MB of RAM when we're done */
|
|
|
|
for (uintptr_t i = 0; i < 0x00100000; i += 0x1000) {
|
|
|
|
alloc_frame(get_page(i, 1, kernel_directory), 1, 0);
|
|
|
|
}
|
|
|
|
}
|