aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Guegan <manzerbredes@mailbox.org>2021-05-02 14:46:18 +0200
committerLoic Guegan <manzerbredes@mailbox.org>2021-05-02 14:46:18 +0200
commite59104ffb55abe522c82d658f1f285149cca2cb1 (patch)
treeeb3cc1e46d0dd5b684864903b95ae32e245010da
parent4f08ba2b1d0ad7ea90d4d97a483b56b891b9c902 (diff)
Debug GDT enable multitasking
-rw-r--r--src/Makefile2
-rw-r--r--src/boucane.cc44
-rw-r--r--src/core/apic.cc30
-rw-r--r--src/core/apic.hpp1
-rw-r--r--src/core/gdt.S22
-rw-r--r--src/core/idt.cc18
-rw-r--r--src/core/idt.hpp1
-rw-r--r--src/core/int.S80
-rw-r--r--src/core/paging.cc12
-rw-r--r--src/core/scheduler.cc91
-rw-r--r--src/core/scheduler.hpp28
-rw-r--r--src/core/scheduler_asm.S27
-rw-r--r--src/core/syscalls.cc8
-rw-r--r--src/drivers/framebuffer.cc4
14 files changed, 283 insertions, 85 deletions
diff --git a/src/Makefile b/src/Makefile
index 0390042..61e6899 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
EXEC := boucane
-CC := g++ -Wno-write-strings -Wno-int-to-pointer-cast -mcmodel=large -nostdlib -nostdinc -no-pie -fno-builtin -fno-stack-protector -I ./ -I include
+CC := g++ -g -Wno-write-strings -Wno-int-to-pointer-cast -mcmodel=large -nostdlib -nostdinc -no-pie -fno-builtin -fno-stack-protector -I ./ -I include
LD_SCRIPT := linker.ld
# Note that BOOT_OBJ do not match boot.S
diff --git a/src/boucane.cc b/src/boucane.cc
index caf0b8d..f4d9482 100644
--- a/src/boucane.cc
+++ b/src/boucane.cc
@@ -28,15 +28,18 @@ extern u64 gdt64_tss;
void configure_tss(){
// Get TSS physical address
- u64 tss_addr=(u64)PHY(&kvar_tss);
+ u64 tss_addr=(u64)&kvar_tss;
u32 limit=sizeof(TSS);
u32 desc1=(tss_addr&0xFFFF)<<16|(limit&0xFFFF);
+ tss_addr>>=16;
u32 desc2=(tss_addr&0xFF);
- desc2|=0b1001<<8; // Type
- desc2|=0b111<<13; // Permission & present
- u32 desc3=((limit>>8)&0xFFFF)|(limit>>24);
-
+ desc2|=0b11101001<<8; // Type, Permission, present
+ tss_addr>>=8;
+ desc2|=tss_addr<<24;
+ tss_addr>>=8;
+ u32 desc3=tss_addr;
+
// Configure GDT
u32 *gdt_entry=(u32*)&gdt64_tss;
gdt_entry[0]=desc1;
@@ -46,6 +49,7 @@ void configure_tss(){
// Configure segment
kvar_tss.rsp0=(u64)VIRT(kvar_stack_pma);
+ kvar_tss.iomap_address=0;
asm(
"mov $0x28, %ax \n\t"
"ltr %ax"
@@ -53,9 +57,15 @@ void configure_tss(){
}
void task1(){
- u64 a=0xEEEEEE;
- DUMP(a);
- while(1);
+ while(1){
+ asm("mov $1, %rdi;int $0x30");
+ }
+}
+
+void task2(){
+ while(1){
+ asm("mov $2, %rdi;int $0x30");
+ }
}
extern "C" void boucane(u64 mb_info){
@@ -77,7 +87,7 @@ extern "C" void boucane(u64 mb_info){
memtext_init();
idt_enable_interrupt();
apic_enable();
-
+
// Looking for framebuffer
FRAMEBUFFER fb_info;
if(mb2_find_framebuffer((u32*)mb_info, &fb_info)){
@@ -97,23 +107,25 @@ extern "C" void boucane(u64 mb_info){
__putchar=vgatext_putchar;
}
}
-
+
// Booting!
printk("Booting Boucane v%d.%d.%d\n",VERSION_MAJOR,VERSION_MINOR, VERSION_PATH);
-
+ printk("System informations -- ");
char bootloader[20];
if(mb2_find_bootloader_name((u32*)mb_info,bootloader)){
- printk("System informations -- BOOT:%s ", bootloader);
+
+ printk("BOOT:%s ", bootloader);
}
-
+
MEM_INFO mem_infos;
if(mb2_find_mem((u32*)mb_info,&mem_infos)){
u64 mem=mem_infos.mem_upper-mem_infos.mem_lower;
mem/=1024;
printk("RAM:%dMB\n", mem);
}
-
- //create_task((void*)task1, 50);
- //scheduler_start();
+ show_ticks=1;
+ create_task((void*)task1, 50);
+ create_task((void*)task2, 50);
+ scheduler_start();
while(1);
} \ No newline at end of file
diff --git a/src/core/apic.cc b/src/core/apic.cc
index 6722a3d..aba801a 100644
--- a/src/core/apic.cc
+++ b/src/core/apic.cc
@@ -17,23 +17,23 @@
u8 lapic_space[4096] __attribute__((aligned(4096)));
u8 ioapic_space[4096] __attribute__((aligned(4096)));
-
+char enable=0;
void apic_enable(){
// Memory Allocation
- PAGE_MAP(lapic_space,APIC_LAPIC_ADDR, PAGING_OPT_DEFAULTS);
- PAGE_MAP(lapic_space,APIC_LAPIC_ADDR,PAGING_OPT_DEFAULTS);
-
+ PAGE_MAP(lapic_space,APIC_LAPIC_ADDR, PAGING_OPT_PCD|PAGING_OPT_DEFAULTS);
+ PAGE_MAP(ioapic_space,APIC_IOAPIC_ADDR,PAGING_OPT_PCD|PAGING_OPT_DEFAULTS);
+
// Configure APIC register location and enable it via MSR
- u64 lapic_addr=(u64)APIC_LAPIC_ADDR;
+ /* u64 lapic_addr=(u64)APIC_LAPIC_ADDR;
u32 high=lapic_addr>>32;
u32 low=((u64)APIC_LAPIC_ADDR&0xFFFFFFFF);
low|=0x800; // Enable apic
- WRITE_MSR(0x1B,high,low);
+ WRITE_MSR(0x1B,high,low);*/
// Configure LAPIC device using mmap
apic_write(APIC_LAPIC_REG_SPURIOUS, 0x100&apic_read(APIC_LAPIC_REG_SPURIOUS));
- apic_write(APIC_DFR, 0xFFFFFFFF);
- apic_write(APIC_PRIOR, 0);
+ //apic_write(APIC_DFR, 0xFFFFFFFF);
+ // apic_write(APIC_PRIOR, 0);
apic_write(APIC_LAPIC_TIMER_DVD, 1);
apic_write(APIC_LAPIC_TIMER_LVT, (1<<17)|61);
apic_write(APIC_LAPIC_TIMER_IC, 100000);
@@ -43,6 +43,7 @@ void apic_enable(){
*ioapic_reg=0x12; // Select the 0x12 IRQ
ioapic_reg=(u32*)(((u64)ioapic_space)+0x10); // Now use the IOREGWIN to write
*ioapic_reg=(0x0<<12)|60; // Enable IRQ 1 (0x12) and assign it to the vector 0x3C (index 60 in the IDT)
+ enable=1;
}
void apic_write(u32 reg, u32 value){
@@ -55,6 +56,15 @@ u32 apic_read(u32 reg){
return *lapic_reg;
}
-extern "C" void ack(){
- apic_write(APIC_EOI, 0);
+extern "C" void apic_ack(){
+ if(enable){
+/* u8 data;
+ do {
+ inb(0x64,data);
+ }
+ while((data&0x01) == 0);
+ inb(0x60,data); */
+
+ apic_write(APIC_EOI, 0);
+ }
} \ No newline at end of file
diff --git a/src/core/apic.hpp b/src/core/apic.hpp
index c64c509..decb565 100644
--- a/src/core/apic.hpp
+++ b/src/core/apic.hpp
@@ -4,6 +4,7 @@
#include "boucane.hpp"
+extern "C" void apic_ack();
void apic_enable();
void apic_write(u32 reg, u32 value);
u32 apic_read(u32 reg); \ No newline at end of file
diff --git a/src/core/gdt.S b/src/core/gdt.S
index c32db59..d99145e 100644
--- a/src/core/gdt.S
+++ b/src/core/gdt.S
@@ -3,31 +3,35 @@
gdt64:
gdt64_null:
- .long 0
- .long 0
+ .word 0xFFF
+ .word 0x0
+ .byte 0
+ .byte 0
+ .byte 1
+ .byte 0
gdt64_cs:
.long 0
.byte 0
- .byte 0b10011100
- .byte 0b00100000
+ .byte 0b10011010 # Present and non-conforming Readable
+ .byte 0b00100000 # Long mode
.byte 0
gdt64_ds:
.long 0
.byte 0
- .byte 0b10010010
+ .byte 0b10010010 # Present, writable
.word 0
gdt64_cs_user:
.long 0
.byte 0
- .byte 0b11111100
- .byte 0b00100000
+ .byte 0b11111010 # Present, Privilege 3 and non-conforming Readable
+ .byte 0b00100000 # Long mode
.byte 0
gdt64_ds_user:
.long 0
.byte 0
- .byte 0b11110010
+ .byte 0b11110010 # Present, Privilege 3 and writable
.word 0
- gdt64_tss:
+ gdt64_tss: # Will be setup in the boucane() function
.long 0
.long 0
.long 0
diff --git a/src/core/idt.cc b/src/core/idt.cc
index 495b332..b8b57c7 100644
--- a/src/core/idt.cc
+++ b/src/core/idt.cc
@@ -6,7 +6,7 @@
u32 idt[IDT_MAX_ENTRIES][4] __attribute__((aligned(4096)));;
IDT_REGISTER IDTR;
-extern u64 INT_DEFAULT,INT_0,INT_14,INT_KBD,INT_CLK;
+extern u64 INT_DEFAULT,INT_0,INT_10,INT_14,INT_KBD,INT_CLK,INT_SYSCALL;
void idt_enable_interrupt(void){
IDTR.base=((u64)idt);
@@ -28,6 +28,10 @@ void idt_enable_interrupt(void){
d.offset=(u64)&INT_14;
idt_write_descriptor(d, i);
}
+ else if(i==10){
+ d.offset=(u64)&INT_10;
+ idt_write_descriptor(d, i);
+ }
else if(i==60){ // Keyboard
d.offset=(u64)&INT_KBD;
idt_write_descriptor(d, i);
@@ -36,12 +40,20 @@ void idt_enable_interrupt(void){
d.offset=(u64)&INT_CLK;
idt_write_descriptor(d, i);
}
+ else if(i==0x30){ // Syscall
+ IDT_DESCRIPTOR d2;
+ d2.ign=0;
+ d2.ist=0;
+ d2.selector=0x08;
+ d2.options=IDT_OPT_P|IDT_OPT_PRVL_3|IDT_OPT_TYPE_INT;
+ d2.offset=(u64)&INT_SYSCALL;
+ idt_write_descriptor(d2, i);
+ }
else {
d.offset=(u64)&INT_DEFAULT;
idt_write_descriptor(d, i);
}
- }
-
+ }
// Enable interrupts
asm(
"lidt (IDTR) \n\t"
diff --git a/src/core/idt.hpp b/src/core/idt.hpp
index 1133ad1..67b52d9 100644
--- a/src/core/idt.hpp
+++ b/src/core/idt.hpp
@@ -9,6 +9,7 @@
#define IDT_OPT_P (1 << 15)
#define IDT_OPT_TYPE_INT 0xE << 8
+#define IDT_OPT_TYPE_TRAP 0xF << 8
#define IDT_OPT_PRVL_0 0
#define IDT_OPT_PRVL_1 (1 << 13)
#define IDT_OPT_PRVL_2 (2 << 13)
diff --git a/src/core/int.S b/src/core/int.S
index 9505191..2897698 100644
--- a/src/core/int.S
+++ b/src/core/int.S
@@ -1,50 +1,112 @@
.code64
.extern printk
-.extern ack
+.extern apic_ack
-.macro call_printk msg
+.macro CALL_PRINTK msg
mov \msg, %rdi
mov $0, %eax # Required for variadic functions
mov $printk,%rcx
call *(%rcx)
.endm
+.macro SAVE_REGS
+ push %r8
+ push %r9
+ push %r10
+ push %r11
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+ push %rax
+ push %rbx
+ push %rcx
+ push %rdx
+ push %rbp
+ push %rsi
+ push %rdi
+ push %rax
+ mov $0x10, %ax
+ mov %ax, %ds
+ pop %rax
+.endm
+
+.macro RESTORE_REGS
+ pop %rdi
+ pop %rsi
+ pop %rbp
+ pop %rdx
+ pop %rcx
+ pop %rbx
+ pop %rax
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %r11
+ pop %r10
+ pop %r9
+ pop %r8
+.endm
+
.globl INT_DEFAULT
INT_DEFAULT:
+ SAVE_REGS
+ RESTORE_REGS
iretq
.globl INT_0
INT_0:
- call_printk $MSG_INT_0
+ CALL_PRINTK $MSG_INT_0
INT_0_INFINITE:
jmp INT_0_INFINITE
iretq
+.globl INT_10
+INT_10:
+ CALL_PRINTK $MSG_INT_10
+ INT_10_INFINITE:
+ jmp INT_10_INFINITE
+ iretq
+
.globl INT_14
INT_14:
- call_printk $MSG_INT_14
- mov $0, %eax
- call printk
+ CALL_PRINTK $MSG_INT_14
INT_14_INFINITE:
jmp INT_14_INFINITE
iretq
.globl INT_KBD
INT_KBD:
- #call_printk $MSG_INT_KBD
- call ack
+ SAVE_REGS
+ CALL_PRINTK $MSG_INT_KBD
+ call apic_ack
+ RESTORE_REGS
iretq
.globl INT_CLK
.extern clock
INT_CLK:
+ SAVE_REGS
call clock
- call ack
+ call apic_ack
+ RESTORE_REGS
+ iretq
+
+.globl INT_SYSCALL
+.extern syscall
+INT_SYSCALL:
+ SAVE_REGS
+ call syscall
+ RESTORE_REGS
iretq
+
MSG_INT_0:
.asciz "Zero Division error!"
+MSG_INT_10:
+.asciz "Invalid TSS!"
MSG_INT_14:
.asciz "Page fault!"
MSG_INT_KBD:
diff --git a/src/core/paging.cc b/src/core/paging.cc
index 7231352..ebd4f9d 100644
--- a/src/core/paging.cc
+++ b/src/core/paging.cc
@@ -52,7 +52,7 @@ void paging_enable() {
// 4096 bytes stack
PAGE_MAP(kvar_kernel_vma-4096, kvar_stack_pma,PAGING_OPT_DEFAULTS);
-
+
// Load new pml4
kpml4=(u64*)((u64)kpages[0]-kvar_kernel_vma);
lpml4(kpml4);
@@ -190,9 +190,15 @@ void paging_allocate_addr(u64* pml4_table, u64 virt, u64 phy, u16 options, char
u64* paging_create_task(int npages){
u64 *pml4=paging_allocate_utable();
- for(int i=0;i<npages;i++){
- paging_allocate_addr(pml4, i*4096, (u64)PAGE_ALLOCATE(), PAGING_OPT_P|PAGING_OPT_RW|PAGING_OPT_US, 0);
+ int i;
+ for(i=0;i<npages;i++){
+ paging_allocate_addr(pml4, i*4096, (u64)PAGE_ALLOCATE(), PAGING_OPT_DEFAULTS|PAGING_OPT_US, 0);
}
+ // Allocate a page for the user stack
+ paging_allocate_addr(pml4, i*4096, (u64)PAGE_ALLOCATE(), PAGING_OPT_DEFAULTS|PAGING_OPT_US, 0);
+ // Allocate a page for the kernel stack
+ paging_allocate_addr(pml4, (i+1)*4096, (u64)PAGE_ALLOCATE(), PAGING_OPT_DEFAULTS, 0);
+
// Enable kernel access
u16 pml4_entry=kvar_kernel_vma>>39&0x1FF;
diff --git a/src/core/scheduler.cc b/src/core/scheduler.cc
index 00741fe..fee5716 100644
--- a/src/core/scheduler.cc
+++ b/src/core/scheduler.cc
@@ -1,40 +1,107 @@
#include "scheduler.hpp"
+#include "boucane.hpp"
+#include "core/apic.hpp"
-TASK tasks[MAX_TASK];
-u32 ntasks=0;
+PROC procs[MAX_TASK];
+u32 nproc=0;
char show_ticks=0;
+char scheduling=0;
+u32 active_process=0;
extern "C" void clock(){
if(show_ticks)
print(".");
+ if(scheduling)
+ schedule();
}
void schedule(){
+ // First get a pointer to the first process saved register.
+ // Since this is called by clock(), %rbp contains a pointer
+ // to the clock() %rbp value and then we access to the registers SAVE_REGS in int.S
+ u64* stack;
+ asm("mov %%rbp, %%rax;mov (%%rax), %%rbx; add $16, %%rbx; mov %%rbx,%0": "=m"(stack)::"rax","rbx");
+
+ // Save current task
+ PROC *t=&procs[active_process];
+ t->registers.r8=stack[0];
+ t->registers.r9=stack[1];
+ t->registers.r10=stack[2];
+ t->registers.r11=stack[3];
+ t->registers.r12=stack[4];
+ t->registers.r13=stack[5];
+ t->registers.r14=stack[6];
+ t->registers.r15=stack[7];
+ t->registers.rdi=stack[8];
+ t->registers.rsi=stack[9];
+ t->registers.rbp=stack[10];
+ t->registers.rdx=stack[11];
+ t->registers.rcx=stack[12];
+ t->registers.rbx=stack[13];
+ t->registers.rax=stack[14];
+ t->registers.rip=stack[15];
+ t->registers.cs=stack[16];
+ t->registers.eflags=stack[17];
+ t->registers.rsp=stack[18];
+ t->registers.ds=stack[19];
+ // Goto next task
+ active_process++;
+ if(active_process>=nproc)
+ active_process=0;
+
+ t=&procs[active_process];
+ kvar_tss.rsp0=t->registers.rsp0;
+
+ // Clock acknownledgement
+ apic_ack();
+
+ asm volatile(
+ "mov %0, %%rdi \n\t"
+ "jmp switch \n\t"
+ :: "a" (t)
+ );
}
void create_task(void* task, u32 size){
- if(ntasks>=MAX_TASK){
+ if(nproc>=MAX_TASK){
printk("Could not create more tasks.");
return;
}
- TASK *t=&tasks[ntasks];
- t->id=ntasks;
- t->pid=ntasks;
+ PROC *t=&procs[nproc];
+ t->id=nproc;
+ t->pid=nproc;
t->size=size;
- t->pml4=paging_create_task(size/4096+1);
+
+ u32 npages=size%4096 ? size/4096 + 1 : size/4096;
+ // Note that paging_create_task() allocate 2 more pages (one for the user stack and
+ // the other for the kernel stack)
+ t->pml4=paging_create_task(npages);
+ t->registers.rsp=TASK_VMA+npages*4096+4096; // User stack
+ t->registers.rsp0=TASK_VMA+npages*4096+4096*2; // Kernel stack on the last page
+
+ t->registers.rip=TASK_VMA;
+ t->registers.cs=0x1B; // 0x18 and 0x3 privilege
+ t->registers.ds=0x23; // 0x20 and 0x3 privilege
// Load task using
lpml4(t->pml4);
memcpy(task, TASK_VMA, size);
lpml4(kpml4);
-
- ntasks++;
+
+ nproc++;
}
void scheduler_start(){
- TASK *t=&tasks[0];
- lpml4(t->pml4);
- asm("jmp switch");
+ scheduling=1;
+ active_process=0;
+ PROC *t=&procs[active_process];
+ kvar_tss.rsp0=t->registers.rsp0;
+ asm(
+ "cli \n\t"
+ "mov %0, %%rdi \n\t"
+ "jmp switch \n\t"
+ :: "r" (t)
+ );
} \ No newline at end of file
diff --git a/src/core/scheduler.hpp b/src/core/scheduler.hpp
index 15b4caf..17b7f96 100644
--- a/src/core/scheduler.hpp
+++ b/src/core/scheduler.hpp
@@ -4,24 +4,32 @@
#define MAX_TASK 5
#define TASK_VMA 0x0
+#if (TASK_VMA % 0x7)
+ #error TASK_VMA value is not 4096 bits aligned!
+#endif
+// DO NOT CHANGE THE FOLLOWING STRUCTURE WITHOUT CONCIDERING UPDATING
+// THE SWITCH FUNCTION INTO scheduler_asm.S
typedef struct {
- u32 rax, rbx, rcx, rdx;
- u32 cs, rip;
- u32 ss, rsp, rbp;
- u32 rsi, rdi;
- u32 ds, es, fs, gs;
- u32 eflags;
- u32 ss0, rsp0;
+ u64 rax, rbx, rcx, rdx;
+ u64 cs, rip;
+ u64 ss, rsp, rbp;
+ u64 rsi, rdi;
+ u64 ds, es, fs, gs;
+ u64 eflags;
+ u64 rsp0;
+ u64 r8,r9,r10,r11,r12,r13,r14,r15;
} __attribute__((packed)) REGS;
+// DO NOT CHANGE THE FOLLOWING STRUCTURE WITHOUT CONCIDERING UPDATING
+// THE SWITCH FUNCTION INTO scheduler_asm.S
typedef struct {
+ u64* pml4;
+ REGS registers;
u32 id;
u32 pid;
- u64* pml4;
u32 size;
- REGS registers;
-} __attribute__((packed)) TASK;
+} __attribute__((packed)) PROC;
extern char show_ticks;
diff --git a/src/core/scheduler_asm.S b/src/core/scheduler_asm.S
index fc8bc50..8f417c4 100644
--- a/src/core/scheduler_asm.S
+++ b/src/core/scheduler_asm.S
@@ -2,26 +2,33 @@
-
+.extern kvar_stack_pma
switch:
-
- mov $0x23, %ax
+ # TODO: Check if we come from kernel mode (use kernel stack)
+ # TODO: restore all registers
+
+ mov 96(%rdi), %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
- push $0x23 #
- push $0x80
+ mov (%rdi), %rax
+ mov %rax, %cr3
+
+ push 96(%rdi)
+ push 64(%rdi)
pushf
pop %rax
- #orl $0x200, %%eax
- mov $0xffffbfff, %rbx
+ or $0x200, %rax # Enable interrupt
+ mov $0xffffffffbfff, %rbx # NT flag
and %rbx, %rax
push %rax
- push $0x1B
- push $0x0
-
+ push 40(%rdi)
+ push 48(%rdi)
+
+
+ # Perform task switching
iretq
diff --git a/src/core/syscalls.cc b/src/core/syscalls.cc
new file mode 100644
index 0000000..f98e6e3
--- /dev/null
+++ b/src/core/syscalls.cc
@@ -0,0 +1,8 @@
+#include "boucane.hpp"
+
+extern "C" void syscall(){
+ u64 call_number;
+ asm volatile("mov %%rdi, %0":"=r"(call_number));
+ printk("%d",call_number);
+ for(int i=0;i<10000000;i++){}
+} \ No newline at end of file
diff --git a/src/drivers/framebuffer.cc b/src/drivers/framebuffer.cc
index ce7790e..33b73d6 100644
--- a/src/drivers/framebuffer.cc
+++ b/src/drivers/framebuffer.cc
@@ -15,7 +15,7 @@ void framebuffer_init(FB_CFG config){
// Ensure we start writing at the begining of the page since
// start is not necessarly 4096 bytes aligned
start=PAGE(start);
- PAGE_RMAP(start,fb_cfg.location, PAGING_OPT_DEFAULTS,fb_cfg.pitch*fb_cfg.height);
+ PAGE_RMAP(start,fb_cfg.location, PAGING_OPT_PCD|PAGING_OPT_DEFAULTS,fb_cfg.pitch*fb_cfg.height);
fb_cfg.location=start;
}
@@ -25,7 +25,7 @@ void framebuffer_draw(FB_PIXEL p){
p.y=p.y>(fb_cfg.width)?fb_cfg.width:p.y;
p.x=p.x<0?0:p.x;
p.y=p.y<0?0:p.y;
-
+
u8 *pixel=(u8*)(fb_cfg.location+p.x*(fb_cfg.depth/8)+p.y*fb_cfg.pitch);
pixel[0]=p.r;
pixel[1]=p.g;