Compare commits

..

93 Commits

Author SHA1 Message Date
Simon
e8135e9ff9 ass 2025-07-08 02:38:04 +02:00
Simon
c38000e503 foooo 2025-07-08 01:57:43 +02:00
Simon
9ffd22e941 adding new vault sch 2025-07-08 01:25:50 +02:00
Simon
698d0c155b fixed scheduler in vault. insert borderline reference 2025-07-08 01:19:05 +02:00
Eggert Jung
68f0381c01 Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-07-08 01:14:30 +02:00
Eggert Jung
bc0a94b705 timer/scheduler working 2025-07-08 01:11:22 +02:00
Simon
2a7d1bdedc fixing typos 2025-07-08 00:40:33 +02:00
Eggert Jung
689c77f19e remove crap 2025-07-07 23:43:33 +02:00
Simon
38a10f2010 anfang aufgabe 5 und notizen 2025-06-27 15:49:07 +02:00
Simon
f5c42a8e77 notes 2025-06-25 17:36:46 +02:00
Eggert Jung
638aa9b636 5 applications running on 4 cores (qemu kvm)
maybe need to add more apps if hardware has more cores
2025-06-23 21:30:43 +02:00
Eggert Jung
7322a9ed70 schedule with 4 application 2025-06-23 21:27:57 +02:00
Eggert Jung
e6d6e7521c scheduler + 2 instances of application 2025-06-23 20:21:19 +02:00
Eggert Jung
da34f7cada fix stack usage and readylist 2025-06-23 14:55:08 +02:00
Eggert Jung
94249eda37 changes 2025-06-22 11:45:31 +02:00
Niklas Gollenstede
d9978ddc37 simplify Qeue, update some comments and include paths 2025-06-17 16:09:25 +02:00
Eggert Jung
68e11c9793 rumgedümpel 2025-06-17 15:58:08 +02:00
Eggert Jung
5b664840d8 progress on scheduler/dispatcher implementation 2025-06-17 15:41:56 +02:00
Eggert Jung
944d4991cb Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-06-17 14:19:42 +02:00
Eggert Jung
76eb2420bd thread implementation 2025-06-17 14:17:30 +02:00
Simon
b422ee176a foooo 2025-06-17 13:59:56 +02:00
Eggert Jung
c1145d28f8 context switching between two functions 2025-06-17 13:56:33 +02:00
Simon
ab76afa30a offset works now 2025-06-17 02:22:47 +02:00
Simon
3d659e0eab gooooo 2025-06-17 02:11:57 +02:00
Simon
e8ec76112b fooo 2025-06-17 01:16:52 +02:00
Eggert Jung
a0e2a59f96 test 2025-06-17 01:15:19 +02:00
Simon
24a0888760 fake fake is missing 2025-06-16 23:55:59 +02:00
Simon
f90eaa3fcd context switch seems to do stuff 2025-06-16 23:27:26 +02:00
Eggert Jung
a38b6cbdb2 vbl 2025-06-16 23:04:11 +02:00
Simon
7c1f380184 fixed really all bugs, for real 2025-06-03 15:05:55 +02:00
Simon
0a150eb84b fixed really all bugs 2025-06-03 13:25:30 +02:00
Simon
c46cc8d1a2 fixed all bugs 2025-06-02 16:13:39 +02:00
Simon
893fc4bad9 added kout to vault 2025-06-01 20:06:58 +02:00
Eggert Jung
83e18391b7 rum 2025-06-01 18:43:46 +02:00
Eggert Jung
c4d7ca3cf0 changes 2025-05-25 17:02:28 +02:00
Eggert Jung
9af6bd4455 Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-05-20 23:21:40 +02:00
Eggert Jung
4ee45e27c2 wip aufg 3 2025-05-20 23:21:25 +02:00
Simon
00c6f9c943 removed interrupt enable/disable from critical app section 2025-05-20 20:33:21 +02:00
Eggert Jung
deac8bfb8c fix ) 2025-05-20 20:21:41 +02:00
Eggert Jung
901eb0bbf5 zack fertig 2025-05-13 17:44:58 +02:00
Eggert Jung
f1e5f17d1f move reboot call to handlerscc 2025-05-13 17:36:34 +02:00
Eggert Jung
a415260776 rename lock 2025-05-13 16:49:28 +02:00
Eggert Jung
2421136335 foo 2025-05-13 16:24:44 +02:00
Eggert Jung
b0f129141a fix hw issue 2025-05-13 16:05:28 +02:00
Eggert Jung
ae3980cfa2 check cga pos bounds and debug print 2025-05-13 15:35:47 +02:00
Eggert Jung
bddcebefae Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-05-13 15:35:03 +02:00
Eggert Jung
7d65db4f45 cleanup code 2025-05-13 15:34:51 +02:00
Simon
e4e7cb9d0c added spinlock 2025-05-13 15:10:03 +02:00
Simon
9afe202078 ticketlock foo 2025-05-13 14:35:58 +02:00
Simon
4a731be0ca removed double lines 2025-05-12 21:44:42 +02:00
Simon
a053ac561c censored 2025-05-12 21:44:13 +02:00
Eggert Jung
20f7f50fa9 start app on appcpu 2025-05-12 21:41:46 +02:00
Eggert Jung
65514ec040 add app 2025-05-11 02:20:07 +02:00
Eggert Jung
b61df61e36 fix endof interrupt 2025-05-11 01:18:41 +02:00
Eggert Jung
220bdd8b77 Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-05-11 01:02:22 +02:00
Eggert Jung
2543dca933 cleanup 2025-05-11 01:01:47 +02:00
Eggert Jung
58f8cf1815 execute fetch in isr 2025-05-10 23:22:36 +02:00
Simon
d5dc4935a5 fixed iret 2025-05-10 22:16:22 +02:00
Eggert Jung
2a0f0573db set IDT 2025-05-10 21:17:52 +02:00
Eggert Jung
ebd3bd3597 remove unneccessary bullshit 2025-05-10 20:03:04 +02:00
Eggert Jung
7214ce8e53 foo 2025-05-10 19:42:48 +02:00
Eggert Jung
383561a4f6 add ctrl alt del 2025-05-10 16:53:17 +02:00
Simon
e9500c1b40 capitalization 2025-05-10 15:37:56 +02:00
Simon
9a311fd38c ich hab das ganz sicher nicht die letzten 5 min gefixed was ich am dienstag vergeigt habe 2025-05-10 15:01:13 +02:00
Eggert Jung
90faf06496 Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-05-06 19:51:13 +02:00
Eggert Jung
b2368b2bc5 implement init 2025-05-06 19:50:56 +02:00
Simon
f8942434df enable interrupts for each core 2025-05-06 19:09:42 +02:00
Simon
1dc52aa6f2 checkmark to done todo 2025-05-06 18:57:25 +02:00
Eggert Jung
24c6311f38 drain 2025-05-06 18:54:09 +02:00
Eggert Jung
ecc3b1011d Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ss25/v_bsb1/Gruppe_018 2025-05-06 18:09:56 +02:00
Eggert Jung
5846351aed feedback 2025-05-06 18:07:37 +02:00
Simon
35765897c4 handler asm code for saving the volatile registers created 2025-05-06 17:53:45 +02:00
Simon
6d5f48e154 handle panic now triggers a panic 2025-05-06 17:27:51 +02:00
Simon
55c23fb293 added the user defined interrupt 2025-05-06 17:04:11 +02:00
Simon
6534b4660c mini changes 2025-05-06 16:32:32 +02:00
Eggert Jung
35c2667fbf copystream for up to 8 cores 2025-04-28 16:04:52 +02:00
Eggert Jung
2c60b88849 ingnore invalid scancodes 2025-04-28 01:49:09 +02:00
Simon
bccd23c0c6 added flush 2025-04-28 00:47:27 +02:00
Simon
35d9c7bd48 first try for ps2, time to debug 2025-04-28 00:30:04 +02:00
Simon
69c1afa5dc removed done todos 2025-04-27 22:05:41 +02:00
Eggert Jung
a3495dbc72 Merge remote-tracking branch 'refs/remotes/origin/main' 2025-04-27 21:10:34 +02:00
Eggert Jung
0b8c00ab19 implement scrollup 2025-04-27 21:09:43 +02:00
Simon
92792d9cac added the qemu tools thingie to gitignore since llvm-gdb is outdated and no longer used by the real cool kids 2025-04-27 20:59:48 +02:00
Eggert Jung
0666fc30c7 fix debug windows 2025-04-27 20:37:25 +02:00
Simon
6b2d3dc8ed removed unneccesary textstream member vars 2025-04-26 18:44:40 +02:00
Eggert Jung
4a537711e4 not working, dout[] 2025-04-26 18:42:24 +02:00
Simon
7add9c8fca wdasda 2025-04-26 18:14:12 +02:00
Eggert Jung
563d30f9f2 add (colored) textwindow 2025-04-26 17:27:56 +02:00
Eggert Jung
9ae9f6fd78 implement \n on textwindow 2025-04-26 17:24:24 +02:00
Simon
2d64c0654f window tests 2025-04-25 18:27:18 +02:00
Simon
6ad2dda8db fix position 2025-04-25 18:22:50 +02:00
Simon
2e81a8b419 window tests 2025-04-25 18:03:20 +02:00
Eggert Jung
4fd8b7d749 rename serial output to sout 2025-04-25 17:53:15 +02:00
54 changed files with 1035 additions and 311 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
.build* .build*
/build* /build*
/tools/qemu.mk
/tools/remote.mk

195
1 Normal file
View File

@@ -0,0 +1,195 @@
#include "arch/lapic.h"
#include "boot/startup_ap.h"
#include "arch/core_interrupt.h"
#include "debug/copystream.h"
#include "debug/output.h"
#include "debug/assert.h"
#include "arch/cga.h"
#include "arch/textwindow.h"
#include "arch/serial.h"
#include "device/serialstream.h"
#include "device/textstream.h"
#include "device/ps2controller.h"
#include "arch/ioapic.h"
#include "thread/scheduler.h"
#include "user/app1/appl.h"
#include "sync/ticketlock.h"
#include "interrupt/guard.h"
#include "arch/context.h"
#include "thread/thread.h"
///TextStream kout = TextStream(0, 80, 0, 10, true);
Ticketlock koutlock;
Scheduler sch;
//TextStream dout[8] = {
// TextStream(0 ,20,12,25,false),
// TextStream(20,40,12,25,false),
// TextStream(40,60,12,25,false),
// TextStream(60,80,12,25,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
//};
TextStream dout[Core::MAX] = {
{0, 40, 10, 14},
{40, 80, 10, 14},
{0, 40, 14, 18},
{40, 80, 14, 18},
{0, 40, 18, 22},
{40, 80, 18, 22},
{0, 40, 22, 25},
{40, 80, 22, 25},
};
CopyStream copystream[Core::MAX]{
{&dout[0], &sout},
{&dout[1], &sout},
{&dout[2], &sout},
{&dout[3], &sout},
{&dout[4], &sout},
{&dout[5], &sout},
{&dout[6], &sout},
{&dout[7], &sout},
};
OutputStream* copyout[Core::MAX]{
&dout[0],
&copystream[1],
&dout[2],
&dout[3],
&dout[4],
&dout[5],
&dout[6],
&dout[7]
};
unsigned int testx, testy;
uint8_t test1_stack[1024];
uint8_t test2_stack[1024];
uint8_t test3_stack[1024];
uint8_t test4_stack[1024];
uint8_t test5_stack[1024];
uint8_t test6_stack[1024];
uint8_t test7_stack[1024];
uint8_t test8_stack[1024];
uint8_t test9_stack[1024];
//Thread test1_thread = Thread(&test1_stack[sizeof(test1_stack)-1]);
Application application1 = Application(&test1_stack[sizeof(test1_stack)-1]);
Application application2 = Application(&test2_stack[sizeof(test2_stack)-1]);
Application application3 = Application(&test3_stack[sizeof(test3_stack)-1]);
Application application4 = Application(&test4_stack[sizeof(test4_stack)-1]);
Application application5 = Application(&test5_stack[sizeof(test5_stack)-1]);
Application application6 = Application(&test6_stack[sizeof(test5_stack)-1]);
Application application7 = Application(&test7_stack[sizeof(test5_stack)-1]);
Application application8 = Application(&test8_stack[sizeof(test5_stack)-1]);
Application application9 = Application(&test9_stack[sizeof(test5_stack)-1]);
//Context test2;
//uint8_t test2_stack[256];
//Thread test2_thread = Thread(&test2_stack[sizeof(test2_stack)-1]);
//void test_func1(){
// while(1){
// {
// Guarded g = Guard::enter();
// g.vault().kout << "test 1\n" << flush;
// }
// context_switch(&test2, &test1);
// }
//}
//
//void test_func2(){
// while(1){
// {
// Guarded g = Guard::enter();
// g.vault().kout << "test 2\n" << flush;
// }
// context_switch(&test1, &test2);
// }
//}
// Main function
// (the bootstrap processor starts here)}
extern "C" int main() {
CGA::setCursor(0, 0);
unsigned int numCPUs = Core::count();
/* Start application processors
* To avoid unexpected behaviour, make sure that interrupts are not
* enabled before the APs are booted. Otherwise it might interfere with the
* Startup IPIs or even block devices like keyboard because of a missing EOI
*/
{
Guarded g = Guard::enter();
sch.ready(&application1);
sch.ready(&application2);
sch.ready(&application3);
sch.ready(&application4);
sch.ready(&application5);
sch.ready(&application6);
sch.ready(&application7);
sch.ready(&application8);
sch.ready(&application9);
}
ApplicationProcessor::boot();
PS2Controller::init();
IOAPIC::init();
IOAPIC::config(1, Core::Interrupt::KEYBOARD);
IOAPIC::allow(1);
Core::Interrupt::enable();
PS2Controller::drainBuffer();
DBG << "Main CPU " << static_cast<int>(LAPIC::getID()) << endl << flush;
{
Guarded g = Guard::enter();
sch.schedule();
}
//Application{}.action();
while (true){
//DBG << "pos: " << testx << ", " << testy << endl << flush;
//Core::pause();
}
return 0;
}
// Main function for application processors
extern "C" int main_ap() {
DBG_VERBOSE << "CPU core " << static_cast<int>(Core::getID()) << " / LAPIC "
<< static_cast<int>(LAPIC::getID()) << " in main_ap()" << endl;
Core::Interrupt::enable();
DBG << "App CPU " << static_cast<int>(Core::getID()) << endl << flush;
{
Guarded g = Guard::enter();
sch.schedule();
}
//assert(Core::getID() != 1);
//Application{}.action();
return 0;
}

View File

@@ -1,5 +1,6 @@
#include "cga.h" #include "cga.h"
#include "arch/ioport.h" #include "../debug/output.h"
#include "../arch/ioport.h"
namespace CGA { namespace CGA {
@@ -8,7 +9,7 @@ namespace CGA {
void writeCGAReg(int reg, int data){ void writeCGAReg(int reg, int data){
index_port.outb(reg); index_port.outb(reg);
data_port.outw(data); data_port.outb(data);
} }
uint16_t readCGAReg(int reg){ uint16_t readCGAReg(int reg){
@@ -17,7 +18,7 @@ namespace CGA {
} }
void setCursor(unsigned abs_x, unsigned abs_y) { void setCursor(unsigned abs_x, unsigned abs_y) {
uint16_t pos = abs_y * COLUMNS + abs_x; uint16_t pos = abs_y * COLUMNS + abs_x;
writeCGAReg(RegisterIndex::CURSOR_LOW, pos & 0xFF); writeCGAReg(RegisterIndex::CURSOR_LOW, pos & 0xFF);
writeCGAReg(RegisterIndex::CURSOR_HIGH, ((pos >> 8) & 0xFF)); writeCGAReg(RegisterIndex::CURSOR_HIGH, ((pos >> 8) & 0xFF));

View File

@@ -95,7 +95,6 @@ void setCursor(unsigned abs_x, unsigned abs_y);
/*! \brief Retrieve the keyboard hardware cursor position on screen /*! \brief Retrieve the keyboard hardware cursor position on screen
* *
* \todo(11) Implement the method using the \ref IOPort
* *
* \param abs_x absolute column of the keyboard hardware cursor * \param abs_x absolute column of the keyboard hardware cursor
* \param abs_y absolute row of the keyboard hardware cursor * \param abs_y absolute row of the keyboard hardware cursor
@@ -118,7 +117,6 @@ void getCursor(unsigned& abs_x, unsigned& abs_y);
* \param character Character to be displayed * \param character Character to be displayed
* \param attrib Attribute with color settings * \param attrib Attribute with color settings
* *
* \todo(11) Implement the method
*/ */
void show(unsigned abs_x, unsigned abs_y, char character, void show(unsigned abs_x, unsigned abs_y, char character,
Attribute attrib = Attribute()); Attribute attrib = Attribute());

View File

@@ -7,14 +7,30 @@
; and populates the registers from the the next context. ; and populates the registers from the the next context.
align 16 align 16
context_switch: context_switch:
mov [rsi + 0], rbx
mov [rsi + 8], rbp
mov [rsi + 16], r12
mov [rsi + 24], r13
mov [rsi + 32], r14
mov [rsi + 40], r15
mov [rsi + 48], rsp
; context_launch populates the register set from the next context structure. ; context_launch populates the register set from the next context structure.
; It does not save the current registers. ; It does not save the current registers.
align 16 ; When only one parameter is used for `align`, it will use NOP align 16 ; When only one parameter is used for `align`, it will use NOP
context_launch: context_launch:
mov rbx, [rdi + 0]
mov rbp, [rdi + 8]
mov r12, [rdi + 16]
mov r13, [rdi + 24]
mov r14, [rdi + 32]
mov r15, [rdi + 40]
mov rsp, [rdi + 48]
ret
; fake_systemv_abi is used to populate the volatile argument registers used by the systemv abi (rdi, rsi, ...) ; fake_systemv_abi is used to populate the volatile argument registers used by the systemv abi (rdi, rsi, ...)
; with values from the non-volatile registers saved within the thread context (r15, r14, ...) ; with values from the non-volatile registers saved within the thread context (r15, r14, ...)
align 16 align 16
fake_systemv_abi: fake_systemv_abi:
mov rdi, r15
mov rsi, r14
ret

View File

@@ -1,9 +1,21 @@
#include "context.h" #include "context.h"
#include "../debug/output.h"
void panic(){
DBG << "panic!\n" << flush;
while(1);
}
void prepareContext(void* tos, Context& context, void (*kickoff)(void*), void prepareContext(void* tos, Context& context, void (*kickoff)(void*),
void* param1) { void* param1) {
(void)tos; ((uint64_t*)tos)[0] = (uint64_t)panic;
(void)context; ((uint64_t*)tos)[-1] = (uint64_t)kickoff;
(void)kickoff; ((uint64_t*)tos)[-2] = (uint64_t)fake_systemv_abi;
(void)param1; context.rsp = (void*)(((uint64_t) tos)-16);
context.rbx = 0;
context.rbp = 0;
context.r12 = 0;
context.r13 = 0;
context.r14 = 0;
context.r15 = (uint64_t)param1;
} }

View File

@@ -28,7 +28,7 @@ constexpr uintptr_t FLAG_ENABLE = 1 << 9;
* *
* \see [ISDMv3, 6.15 Exception and Interrupt * \see [ISDMv3, 6.15 Exception and Interrupt
* Reference](intel_manual_vol3.pdf#page=203) * Reference](intel_manual_vol3.pdf#page=203)
* \todo(12) Add Keyboard and Panic vector numbers * \todo
*/ */
enum Vector { enum Vector {
// Predefined Exceptions // Predefined Exceptions
@@ -67,6 +67,10 @@ enum Vector {
SECURITY_EXCEPTION = 31, SECURITY_EXCEPTION = 31,
// Interrupts // Interrupts
KEYBOARD=32,
PANIC=33,
TIMER=34,
ASS=25
}; };
constexpr size_t VECTORS = 256; constexpr size_t VECTORS = 256;

View File

@@ -1,4 +1,7 @@
#include "ioapic.h" #include "ioapic.h"
#include "apic.h"
#include "core.h"
#include "../debug/assert.h"
namespace IOAPIC { namespace IOAPIC {
/*! \brief IOAPIC registers memory mapped into the CPU's address space. /*! \brief IOAPIC registers memory mapped into the CPU's address space.
@@ -20,22 +23,66 @@ volatile Register *IOWIN_REG =
// IOAPIC manual, p. 8 // IOAPIC manual, p. 8
const Index IOAPICID_IDX = 0x00; const Index IOAPICID_IDX = 0x00;
const Index IOREDTBL_IDX = 0x10; const Index IOREDTBL_IDX = 0x10;
const Index IOREDTBL_ENTRY_SIZE = 0x02;
const uint8_t slot_max = 24; const uint8_t slot_max = 24;
void init() {} RedirectionTableEntry readEntry(Index slot) {
*IOREGSEL_REG = IOREDTBL_IDX + slot * IOREDTBL_ENTRY_SIZE;
void config(uint8_t slot, Core::Interrupt::Vector vector, Register low = *IOWIN_REG;
TriggerMode trigger_mode, Polarity polarity) { *IOREGSEL_REG += IOREDTBL_ENTRY_SIZE / 2;
(void)slot; Register high = *IOWIN_REG;
(void)vector; return RedirectionTableEntry{low, high};
(void)trigger_mode;
(void)polarity;
} }
void allow(uint8_t slot) { (void)slot; } void writeEntry(Index slot, RedirectionTableEntry entry) {
*IOREGSEL_REG = IOREDTBL_IDX + slot * IOREDTBL_ENTRY_SIZE;
*IOWIN_REG = entry.value_low;
*IOREGSEL_REG += IOREDTBL_ENTRY_SIZE / 2;
*IOWIN_REG = entry.value_high;
}
void forbid(uint8_t slot) { (void)slot; } void init() {
for (uint8_t slot = 0; slot < slot_max; slot++) {
RedirectionTableEntry entry = readEntry(slot);
entry.destination = (1 << Core::count()) - 1;
entry.interrupt_mask = MASKED;
entry.trigger_mode = EDGE;
entry.polarity = HIGH;
entry.destination_mode = LOGICAL;
entry.delivery_mode = LOWEST_PRIORITY;
entry.vector = Core::Interrupt::PANIC;
writeEntry(slot, entry);
}
*IOREGSEL_REG = IOAPICID_IDX;
Identification IOAPICID{*IOWIN_REG};
IOAPICID.id = APIC::getIOAPICID();
*IOWIN_REG = IOAPICID.value;
}
void config(uint8_t slot, Core::Interrupt::Vector vector,
TriggerMode trigger_mode, Polarity polarity) {
assert(slot < slot_max);
RedirectionTableEntry entry = readEntry(slot);
entry.vector = vector;
entry.trigger_mode = trigger_mode;
entry.polarity = polarity;
writeEntry(slot, entry);
}
void allow(uint8_t slot) {
assert(slot < slot_max);
RedirectionTableEntry entry = readEntry(slot);
entry.interrupt_mask = UNMASKED;
writeEntry(slot, entry);
}
void forbid(uint8_t slot) {
assert(slot < slot_max);
RedirectionTableEntry entry = readEntry(slot);
entry.interrupt_mask = MASKED;
writeEntry(slot, entry);
}
bool status(uint8_t slot) { bool status(uint8_t slot) {
(void)slot; (void)slot;

View File

@@ -45,7 +45,7 @@ void init();
* \param polarity Polarity of the interrupt signaling (active high or * \param polarity Polarity of the interrupt signaling (active high or
active low) active low)
* *
* \todo(12) Implement Function * \todo Implement Function
*/ */
void config(uint8_t slot, Core::Interrupt::Vector vector, void config(uint8_t slot, Core::Interrupt::Vector vector,
TriggerMode trigger_mode = TriggerMode::EDGE, TriggerMode trigger_mode = TriggerMode::EDGE,
@@ -57,7 +57,7 @@ void config(uint8_t slot, Core::Interrupt::Vector vector,
* To fully enable interrupt handling, the interrupts must be enabled for every * To fully enable interrupt handling, the interrupts must be enabled for every
* CPU (e.g., by calling * CPU (e.g., by calling
* \ref Core::Interrupt::enable() in main). * \ref Core::Interrupt::enable() in main).
* \todo(12) Do that somewhere appropriate. * \todo Do that somewhere appropriate.
* *
* \param slot Number of the external interrupt that should be enabled. * \param slot Number of the external interrupt that should be enabled.
* *

View File

@@ -156,7 +156,7 @@ void set(uint32_t counter, uint8_t divide, uint8_t vector, bool periodic,
* *
* Initializes the \ref LAPIC::Timer * Initializes the \ref LAPIC::Timer
* in such a way that regular interrupts are triggered approx. every `us` * in such a way that regular interrupts are triggered approx. every `us`
* microseconds when \ref LAPIC::Timer:::activate() is called. * microseconds when \ref LAPIC::Timer::activate() is called.
* For this purpose, a suitable timer divisor is determined * For this purpose, a suitable timer divisor is determined
* based on the timer frequency determined with \ref LAPIC::Timer::ticks(). * based on the timer frequency determined with \ref LAPIC::Timer::ticks().
* This timer divisor has to be as small as possible, but large enough to * This timer divisor has to be as small as possible, but large enough to

View File

@@ -1,5 +1,9 @@
#include "lapic.h" #include "lapic.h"
#include "lapic_registers.h" #include "lapic_registers.h"
#include "core.h"
#include "pit.h"
#include "../debug/output.h"
namespace LAPIC { namespace LAPIC {
namespace Timer { namespace Timer {
@@ -56,36 +60,118 @@ static const Register div_masks[] = {
* \param div Divider, must be power of two: 1, 2, 4, 8, 16, 32, 64, 128 * \param div Divider, must be power of two: 1, 2, 4, 8, 16, 32, 64, 128
* \return Bit mask for LAPIC::setTimer() or `0xff` if `div` is invalid. * \return Bit mask for LAPIC::setTimer() or `0xff` if `div` is invalid.
*/ */
Register getClockDiv(uint8_t div) { static Register getClockDiv(uint8_t div) {
(void)div; switch (div) {
return 0; case 1: return div_masks[0];
case 2: return div_masks[1];
case 4: return div_masks[2];
case 8: return div_masks[3];
case 16: return div_masks[4];
case 32: return div_masks[5];
case 64: return div_masks[6];
case 128: return div_masks[7];
default : return 0xff;
}
}
/*! \brief Calculate the LAPIC-timer divider for the bit mask.
* \param div_mask The bit mask, must be one of: 0xb, 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa
* \return LAPIC-timer divider or `0xff` if `div_mask` is invalid.
*/
static uint8_t fromClockDiv(Register div_mask) {
if (div_mask == div_masks[0]) return 1;
if (div_mask == div_masks[1]) return 2;
if (div_mask == div_masks[2]) return 4;
if (div_mask == div_masks[3]) return 8;
if (div_mask == div_masks[4]) return 16;
if (div_mask == div_masks[5]) return 32;
if (div_mask == div_masks[6]) return 64;
if (div_mask == div_masks[7]) return 128;
return 0xff;
} }
uint32_t ticks(void) { uint32_t ticks(void) {
uint32_t ticks = 0; // ticks per millisecond static uint32_t ticks = 0; // ticks per millisecond
// Calculation (Assignment 5) if (ticks != 0) return ticks;
// Prepare Counter
LAPIC::Timer::set(0, 1, 0, false, true);
// Set timer for 10ms
PIT::set(10 * 1000);
// Start LAPIC-Timer
LAPIC::write(TIMER_INITIAL_COUNTER, UINT32_MAX);
PIT::waitForTimeout();
// Get final Count
uint32_t counter = LAPIC::read(TIMER_CURRENT_COUNTER);
// Disable LAPIC-Timer
LAPIC::write(TIMER_INITIAL_COUNTER, 0);
// Calculate tick count.
ticks = (UINT32_MAX - counter) / 10;
DBG << "LAPIC-Timer calibration using PIT" << endl;
return ticks; return ticks;
} }
void set(uint32_t counter, uint8_t divide, uint8_t vector, bool periodic, void set(uint32_t counter, uint8_t divide, uint8_t vector, bool periodic,
bool masked) { bool masked) {
(void)counter; if (divide == 0 || (divide & (divide - 1)) != 0) {
(void)divide; // Not a power of 2
(void)vector; return;
(void)periodic; }
(void)masked;
ControlRegister tcr;
tcr.vector = Core::Interrupt::Vector::TIMER;
tcr.timer_mode = periodic ? PERIODIC : ONE_SHOT;
tcr.masked = masked ? MASKED : NOT_MASKED;
LAPIC::write(TIMER_CONTROL, tcr.value);
LAPIC::write(TIMER_DIVIDE_CONFIGURATION, getClockDiv(divide));
LAPIC::write(TIMER_INITIAL_COUNTER, counter);
} }
bool setup(uint32_t us) { bool setup(uint32_t us) {
(void)us; uint64_t timer_ticks = (static_cast<uint64_t>(ticks()) * us) / 1000ULL;
return false;
uint8_t divisor = 1;
while (timer_ticks > UINT32_MAX) {
// While timer_ticks is to large to fit in 32bits.
timer_ticks >>= 1;
divisor <<= 1;
}
if (divisor > 128)
return false; // Timer interval is to large.
// Setup Masked interrupts to effectively disable the timer.
set(static_cast<uint32_t>(timer_ticks), divisor, Core::Interrupt::TIMER, true, true);
return true;
} }
uint32_t interval() { return 0; } uint32_t interval() {
uint32_t timer_ticks = LAPIC::read(TIMER_INITIAL_COUNTER);
uint32_t divisor = fromClockDiv(LAPIC::read(TIMER_DIVIDE_CONFIGURATION));
void activate() {} uint64_t us = (static_cast<uint64_t>(timer_ticks) * divisor * 1000ULL) / static_cast<uint64_t>(ticks());
return static_cast<uint32_t>(us);
}
void setMasked(bool masked) { (void)masked; } void activate() {
uint32_t timer_ticks = LAPIC::read(TIMER_INITIAL_COUNTER);
// Disable Counter to avoid spouriose interrupts.
LAPIC::write(TIMER_INITIAL_COUNTER, 0);
// Activate Timer interrupts.
setMasked(false);
// enable counter with correct value.
LAPIC::write(TIMER_INITIAL_COUNTER, timer_ticks);
}
void setMasked(bool masked) {
ControlRegister tcr;
tcr.value = LAPIC::read(TIMER_CONTROL);
tcr.masked = masked ? MASKED : NOT_MASKED;
LAPIC::write(TIMER_CONTROL, tcr.value);
}
} // namespace Timer } // namespace Timer
} // namespace LAPIC } // namespace LAPIC

View File

@@ -22,7 +22,6 @@ Serial::Serial(ComPort port, BaudRate baud_rate, DataBits data_bits,
// FIFO: Enable & clear buffers // FIFO: Enable & clear buffers
writeReg(FIFO_CONTROL_REGISTER, writeReg(FIFO_CONTROL_REGISTER,
ENABLE_FIFO | CLEAR_RECEIVE_FIFO | CLEAR_TRANSMIT_FIFO); ENABLE_FIFO | CLEAR_RECEIVE_FIFO | CLEAR_TRANSMIT_FIFO);
// Modem Control: OUT2 (0000 1000) must be set for interrupt // Modem Control: OUT2 (0000 1000) must be set for interrupt
writeReg(MODEM_CONTROL_REGISTER, OUT_2); writeReg(MODEM_CONTROL_REGISTER, OUT_2);
} }
@@ -44,9 +43,8 @@ int Serial::write(char out) {
if(out == '\n') if(out == '\n')
write('\r'); write('\r');
if(readReg(LINE_STATUS_REGISTER) & TRANSMITTER_EMPTY){ while(!(readReg(LINE_STATUS_REGISTER) & TRANSMITTER_EMPTY));
writeReg(TRANSMIT_BUFFER_REGISTER, out);
return out; writeReg(TRANSMIT_BUFFER_REGISTER, out);
} return out;
return -1;
} }

View File

@@ -161,7 +161,6 @@ class Serial {
/*! \brief Read value from register /*! \brief Read value from register
* *
* \todo(11) Implement Method
* *
* \param reg Register index * \param reg Register index
* \return The value read from register * \return The value read from register
@@ -170,7 +169,6 @@ class Serial {
/*! \brief Write value to register /*! \brief Write value to register
* *
* \todo(11) Implement Method
* *
* \param reg Register index * \param reg Register index
* \param out value to be written * \param out value to be written
@@ -188,7 +186,6 @@ class Serial {
* parameters used for the serial connection. Default values are `8N1` (8 bit, * parameters used for the serial connection. Default values are `8N1` (8 bit,
* no parity bit, one stop bit) with 115200 Baud using COM1. * no parity bit, one stop bit) with 115200 Baud using COM1.
* *
* \todo(11) - Implement Constructor
*/ */
explicit Serial(ComPort port = COM1, BaudRate baud_rate = BAUD_115200, explicit Serial(ComPort port = COM1, BaudRate baud_rate = BAUD_115200,
DataBits data_bits = DATA_8BIT, DataBits data_bits = DATA_8BIT,
@@ -196,7 +193,6 @@ class Serial {
/*! \brief Write one byte to the serial interface /*! \brief Write one byte to the serial interface
* *
* \todo(11) - Implement Method
* *
* \param out Byte to be written * \param out Byte to be written
* \return Byte written (or `-1` if writing byte failed) * \return Byte written (or `-1` if writing byte failed)

View File

@@ -1,5 +1,7 @@
#include "textwindow.h" #include "textwindow.h"
#include "cga.h" #include "cga.h"
#include "../utils/string.h"
#include "../debug/output.h"
TextWindow::TextWindow(unsigned from_col, unsigned to_col, unsigned from_row, TextWindow::TextWindow(unsigned from_col, unsigned to_col, unsigned from_row,
unsigned to_row, bool use_cursor) { unsigned to_row, bool use_cursor) {
@@ -8,14 +10,27 @@ TextWindow::TextWindow(unsigned from_col, unsigned to_col, unsigned from_row,
this->from_row = from_row; this->from_row = from_row;
this->to_row = to_row; this->to_row = to_row;
this->use_cursor = use_cursor; this->use_cursor = use_cursor;
setPos(0,0);
} }
extern unsigned int testx, testy;
void TextWindow::setPos(unsigned rel_x, unsigned rel_y) { void TextWindow::setPos(unsigned rel_x, unsigned rel_y) {
unsigned abs_x = from_col + rel_x;
unsigned abs_y = from_row + rel_y;
if(abs_x >= CGA::COLUMNS)
return;
if(abs_y >= CGA::ROWS)
return;
if(use_cursor){ if(use_cursor){
CGA::setCursor(from_col + rel_x, from_row + rel_y); testx = abs_x;
testy = abs_y;
CGA::setCursor(abs_x, abs_y);
}else{ }else{
pos_x = from_col + rel_x; pos_x = abs_x;
pos_y = from_row + rel_y; pos_y = abs_y;
} }
} }
@@ -47,15 +62,34 @@ void TextWindow::getPos(int& rel_x, int& rel_y) const {
rel_y = y; rel_y = y;
} }
void TextWindow::scrollUp(){
for(uint8_t row = from_row; row < to_row-1; row++){
memmove(&CGA::TEXT_BUFFER_BASE[row*CGA::COLUMNS + from_col], &CGA::TEXT_BUFFER_BASE[(row+1)*CGA::COLUMNS + from_col], 2*(to_col-from_col));
}
memset(&CGA::TEXT_BUFFER_BASE[(to_row-1)*CGA::COLUMNS + from_col] , 0, 2*(to_col-from_col));
}
void TextWindow::print(const char* str, size_t length, CGA::Attribute attrib) { void TextWindow::print(const char* str, size_t length, CGA::Attribute attrib) {
for(unsigned i=0; i<length; i++){ for(unsigned i=0; i<length; i++){
unsigned x_now, y_now; unsigned x_now, y_now;
getPos(x_now, y_now); getPos(x_now, y_now);
CGA::show(x_now, y_now, str[i], attrib); if(str[i] == '\n'){
x_now = 0;
if(from_row + y_now >= to_row-1){
scrollUp();
}
else{
y_now++;
}
setPos(x_now, y_now);
return;
}
CGA::show(from_col + x_now, from_row + y_now, str[i], attrib);
if(from_col+x_now >= to_col-1){ if(from_col+x_now >= to_col-1){
x_now = 0; x_now = 0;
if(from_row + y_now >= to_row-1){ if(from_row + y_now >= to_row-1){
//TODO scrollUp() scrollUp();
} }
else{ else{
y_now++; y_now++;

View File

@@ -29,6 +29,8 @@ class TextWindow {
unsigned pos_x; unsigned pos_x;
unsigned pos_y; unsigned pos_y;
void scrollUp();
public: public:
/*! \brief Constructor of a text window /*! \brief Constructor of a text window
* *

View File

@@ -1,4 +1,4 @@
#include "boot/multiboot/data.h" #include "./data.h"
/*! \brief Multiboot Information Structure according to Specification /*! \brief Multiboot Information Structure according to Specification
* \see [Multiboot Specification]{@ref multiboot} * \see [Multiboot Specification]{@ref multiboot}

3
compile_flags.txt Normal file
View File

@@ -0,0 +1,3 @@
-Iarch
-xc++
-std=gnu++2b

View File

@@ -1,4 +1,5 @@
#include "assert.h" #include "assert.h"
#include "output.h"
[[noreturn]] void assertion_failed(const char* exp, const char* func, [[noreturn]] void assertion_failed(const char* exp, const char* func,
const char* file, int line) { const char* file, int line) {
@@ -6,6 +7,7 @@
(void)func; (void)func;
(void)file; (void)file;
(void)line; (void)line;
DBG << "Assertion failed: " << exp << "\nin function: " << func << "\nfrom file: \"" << file << "\" in line " << dec << line << flush;
// TODO: Print error message (in debug window) // TODO: Print error message (in debug window)
// TODO: Then stop the current core permanently // TODO: Then stop the current core permanently
// Use appropriate method from class Core to do so. // Use appropriate method from class Core to do so.

View File

@@ -17,7 +17,7 @@
#define kernelpanic(MSG) \ #define kernelpanic(MSG) \
do { \ do { \
DBG << "PANIC: '" << (MSG) << "' in " << __func__ << " @ " << __FILE__ \ DBG << "PANIC: '" << (MSG) << "' in " << __func__ << " @ " << __FILE__ \
<< ":" << __LINE__ << ") - CPU stopped." << endl; \ << ":" << dec << __LINE__ << ") - CPU stopped." << endl; \
Core::die(); \ Core::die(); \
} while (0) } while (0)

View File

@@ -43,10 +43,10 @@
* possible that the debug output in a multi core system is displayed * possible that the debug output in a multi core system is displayed
* on the wrong (previous) core. * on the wrong (previous) core.
*/ */
#define DBG nullstream
#include "../arch/core.h" #include "../arch/core.h"
#include "../device/textstream.h" #include "../device/textstream.h"
#define DBG *copyout[Core::getID()]
/*! \brief Debug window for the CGA screen /*! \brief Debug window for the CGA screen
* *
@@ -72,7 +72,6 @@
* without any overlap and should be able to display at least 3 lines. * without any overlap and should be able to display at least 3 lines.
* In \MPStuBS, two windows can be placed side-by-side, having 40 columns each. * In \MPStuBS, two windows can be placed side-by-side, having 40 columns each.
* *
* \todo(11) Define `dout`
*/ */
extern TextStream dout[Core::MAX]; extern TextStream dout[Core::MAX];
@@ -83,11 +82,11 @@ extern TextStream dout[Core::MAX];
* an array in the multi core case, which consists of three TextStreams and * an array in the multi core case, which consists of three TextStreams and
* one CopyStream. * one CopyStream.
* For that, construction is done like: * For that, construction is done like:
* \todo(11) Define `copyout`
* *
* \code{.cpp} * \code{.cpp}
* OutputStream* copyout[Core::MAX]{&dout[0], &dout[1], ...} * OutputStream* copyout[Core::MAX]{&dout[0], &dout[1], ...}
* \endcode * \endcode
* *
* \todo(11) Define `copyout`
*/ */
extern OutputStream* copyout[Core::MAX]; extern OutputStream* copyout[Core::MAX];

View File

@@ -1,5 +1,6 @@
#include "ps2controller.h" #include "ps2controller.h"
#include "../arch/system.h"
#include "../arch/core_interrupt.h" #include "../arch/core_interrupt.h"
#include "../arch/ioport.h" #include "../arch/ioport.h"
#include "../compiler/fix.h" #include "../compiler/fix.h"
@@ -89,8 +90,11 @@ enum ControllerCommand {
* *
* \param value data to be sent * \param value data to be sent
*/ */
[[maybe_unused]] static void sendData(uint8_t value) { static void sendData(uint8_t value) {
// TODO: You have to implement this method
if (!(ctrl_port.inb()&HAS_OUTPUT)) {
data_port.outb(value);
}
(void)value; (void)value;
} }
@@ -105,9 +109,20 @@ void init() {
} }
bool fetch(Key &pressed) { bool fetch(Key &pressed) {
// TODO: You have to implement this method uint8_t status_reg = ctrl_port.inb();
(void)pressed; if(!(status_reg & HAS_OUTPUT) )
return false; return false;
DBG_VERBOSE << "status: " << hex << static_cast<int>(status_reg) << "\n" << flush;
uint8_t out_buffer = data_port.inb();
DBG_VERBOSE << "scancode: " << hex << static_cast<int>(out_buffer) << "\n" << flush;
if (status_reg & IS_MOUSE)
return false;
pressed = key_decoder.decode(out_buffer);
return true;
} }
void setRepeatRate(Speed speed, Delay delay) { void setRepeatRate(Speed speed, Delay delay) {
@@ -125,6 +140,8 @@ void setLed(enum LED led, bool on) {
sendData(leds); // Parameter sendData(leds); // Parameter
} }
void drainBuffer() {} void drainBuffer() {
while(ctrl_port.inb() & HAS_OUTPUT)
data_port.inb();
}
} // namespace PS2Controller } // namespace PS2Controller

View File

@@ -146,7 +146,6 @@ void setLed(enum LED led, bool on);
* should be emptied once right before allowing keyboard interrupts * should be emptied once right before allowing keyboard interrupts
* (even if keystrokes might be lost). * (even if keystrokes might be lost).
* *
* \todo(12) Implement method
*/ */
void drainBuffer(); void drainBuffer();

View File

@@ -5,17 +5,17 @@ SerialStream::SerialStream(ComPort port, BaudRate baud_rate, DataBits data_bits,
StopBits stop_bits, Parity parity) StopBits stop_bits, Parity parity)
:Serial(port, baud_rate, data_bits, stop_bits, parity) {} :Serial(port, baud_rate, data_bits, stop_bits, parity) {}
SerialStream kout = SerialStream(); SerialStream sout = SerialStream();
void SerialStream::flush() { void SerialStream::flush() {
print(kout.buffer, kout.pos); print(sout.buffer, sout.pos);
kout.pos = 0; sout.pos = 0;
} }
void SerialStream::setForeground(Color c) { void SerialStream::setForeground(Color c) {
write(0x1b); write(0x1b);
write('['); write('[');
kout << 30+c; sout << 30+c;
flush(); flush();
write('m'); write('m');
} }
@@ -41,18 +41,13 @@ void SerialStream::reset() {
} }
void SerialStream::setPos(int x, int y) { void SerialStream::setPos(int x, int y) {
//char out[] = {0x1b, '[', 0, 0, ';', 0, 0, 'H', 0};
//*this << 0x1b;
write(0x1b); write(0x1b);
kout << '['; sout << '[';
kout << dec << x; sout << dec << x;
kout << ';'; sout << ';';
kout << dec << y; sout << dec << y;
kout << 'H'; sout << 'H';
flush(); flush();
//itoa(x, &out[2], 10);
//itoa(y, &out[5], 10);
//print( out , strlen(out));
} }
void SerialStream::print(char* str, int length) { void SerialStream::print(char* str, int length) {

View File

@@ -61,7 +61,6 @@ class SerialStream : public OutputStream, public Serial {
* *
* Sets up the serial connection as well * Sets up the serial connection as well
* *
* \todo(11) Implement Method
*/ */
explicit SerialStream(ComPort port = COM1, BaudRate baud_rate = BAUD_115200, explicit SerialStream(ComPort port = COM1, BaudRate baud_rate = BAUD_115200,
DataBits data_bits = DATA_8BIT, DataBits data_bits = DATA_8BIT,
@@ -74,13 +73,11 @@ class SerialStream : public OutputStream, public Serial {
* The method is automatically called when the buffer is full, * The method is automatically called when the buffer is full,
* but can also be called explicitly to force output of the current buffer. * but can also be called explicitly to force output of the current buffer.
* *
* \todo(11) Implement Method
*/ */
void flush() override; void flush() override;
/*! \brief Change foreground color (for subsequent output) /*! \brief Change foreground color (for subsequent output)
* *
* \todo(11) Implement Method
* *
* \param c Color * \param c Color
*/ */
@@ -88,7 +85,6 @@ class SerialStream : public OutputStream, public Serial {
/*! \brief Change background color (for subsequent output) /*! \brief Change background color (for subsequent output)
* *
* \todo(11) Implement Method
* *
* \param c Color * \param c Color
*/ */
@@ -96,7 +92,6 @@ class SerialStream : public OutputStream, public Serial {
/*! \brief Change text attribute (for subsequent output) /*! \brief Change text attribute (for subsequent output)
* *
* \todo(11) Implement Method
* *
* \param a Attribute * \param a Attribute
*/ */
@@ -107,7 +102,6 @@ class SerialStream : public OutputStream, public Serial {
* Clear screen, place cursor at the beginning and reset colors * Clear screen, place cursor at the beginning and reset colors
* and attributes to the default value. * and attributes to the default value.
* *
* \todo(11) Implement Method
*/ */
void reset(); void reset();
@@ -116,7 +110,6 @@ class SerialStream : public OutputStream, public Serial {
* \param x Column in window * \param x Column in window
* \param y Row in window * \param y Row in window
* *
* \todo(11) Implement Method
*/ */
void setPos(int x, int y); void setPos(int x, int y);
@@ -142,5 +135,5 @@ class SerialStream : public OutputStream, public Serial {
void print(char* str, int length); void print(char* str, int length);
}; };
extern SerialStream kout; extern SerialStream sout;

View File

@@ -1,4 +1,5 @@
#include "textstream.h" #include "textstream.h"
#include "../arch/core.h"
TextStream::TextStream(unsigned from_col, TextStream::TextStream(unsigned from_col,
unsigned to_col, unsigned to_col,
@@ -9,10 +10,14 @@ TextStream::TextStream(unsigned from_col,
to_col, to_col,
from_row, from_row,
to_row, to_row,
use_cursor){} use_cursor){
//CGA::Color fg = static_cast<CGA::Color>((LAPIC::getID() + 1 ));
//this->reset(' ', CGA::Attribute(CGA::LIGHT_GREEN, fg, false));
}
void TextStream::flush() { void TextStream::flush() {
print(buffer,pos); CGA::Color fg = static_cast<CGA::Color>((Core::getID() + 1 ));
print(buffer,pos,CGA::Attribute(CGA::LIGHT_GREEN, fg, false));
pos = 0; pos = 0;
} }

View File

@@ -20,7 +20,7 @@
* \ref TextWindow and only implements the method \ref TextStream::flush(). * \ref TextWindow and only implements the method \ref TextStream::flush().
* Further formatting or special effects are implemented in \ref TextWindow. * Further formatting or special effects are implemented in \ref TextWindow.
*/ */
class TextStream: public OutputStream, protected TextWindow { class TextStream: public OutputStream, public TextWindow {
// Prevent copies and assignments // Prevent copies and assignments
TextStream(const TextStream&) = delete; TextStream(const TextStream&) = delete;
TextStream& operator=(const TextStream&) = delete; TextStream& operator=(const TextStream&) = delete;
@@ -36,14 +36,7 @@ class TextStream: public OutputStream, protected TextWindow {
* but can also be called explicitly to force output of the current buffer. * but can also be called explicitly to force output of the current buffer.
* *
* *
* \todo(11) Implement method
*/ */
void flush(); void flush();
private:
unsigned from_col;
unsigned to_col;
unsigned from_row;
unsigned to_row;
bool use_cursor;
}; };

View File

@@ -1,12 +1,35 @@
#include "epilogues.h" #include "epilogues.h"
#include "../debug/output.h"
#include "guard.h" #include "guard.h"
#include "../thread/scheduler.h"
extern Key kout_key;
namespace Epilogues { namespace Epilogues {
void keyboard(Vault& g) { (void)g; } void keyboard(Vault& v) {
v.kout.setPos(0,0);
v.kout << kout_key.ascii() << flush ;
void timer(Vault& g) { (void)g; } }
void assassin(Vault& g) { (void)g; } void timer(Vault& v) {
static int counter = 0;
int x, y;
if(Core::getID() == 0) {
v.kout.getPos(x, y);
v.kout.setPos(65U, 0U);
v.kout << counter++ << " " << flush;
v.kout.setPos(x, y);
}
v.sch.resume(true);
}
void assassin(Vault& v) {
if (v.sch.active()->kill_flag) {
v.sch.exit();
}
}
}; // namespace Epilogues }; // namespace Epilogues

View File

@@ -28,7 +28,7 @@ namespace Epilogues {
* \todo(15) Store the key to the keyboard buffer for user threads. Wake user * \todo(15) Store the key to the keyboard buffer for user threads. Wake user
* threads waiting for a key using the key semaphore. * threads waiting for a key using the key semaphore.
* *
* @param g * @param v The vault.
*/ */
void keyboard(Vault& g); void keyboard(Vault& g);
@@ -37,7 +37,7 @@ void keyboard(Vault& g);
* \todo(15) Preemptively reschedule threads * \todo(15) Preemptively reschedule threads
* \todo(16) Check the bellringer * \todo(16) Check the bellringer
* \todo(17) Refresh screen with fixed FPS rate * \todo(17) Refresh screen with fixed FPS rate
* @param g * @param v The vault.
*/ */
void timer(Vault& g); void timer(Vault& g);
@@ -45,6 +45,7 @@ void timer(Vault& g);
* it is set. * it is set.
* *
* \todo(15) Implement the rescheduling (in \MPStuBS only) * \todo(15) Implement the rescheduling (in \MPStuBS only)
* @param v The vault.
*/ */
void assassin(Vault& g); void assassin(Vault& g);
}; // namespace Epilogues }; // namespace Epilogues

View File

@@ -4,6 +4,7 @@
#include "../debug/output.h" #include "../debug/output.h"
#include "../object/bbuffer.h" #include "../object/bbuffer.h"
#include "../sync/ticketlock.h" #include "../sync/ticketlock.h"
#include "../arch/core_interrupt.h"
#include "epilogues.h" #include "epilogues.h"
#define FOR_CURRENT_CORE [Core::getID()] #define FOR_CURRENT_CORE [Core::getID()]
@@ -17,14 +18,57 @@ static BBuffer<Epilogue, 32> epilogue_queue[Core::MAX] = {};
constinit Ticketlock global_lock; constinit Ticketlock global_lock;
constinit bool epi_flag[Core::MAX] = {false}; constinit bool epi_flag[Core::MAX] = {false};
Vault::Vault() {} Vault::Vault() {
}
Guarded::~Guarded() { Guard::leave(); } Guarded::~Guarded() { Guard::leave(); }
Guarded Guard::enter() { while (true); } Guarded Guard::enter() {
epi_flag FOR_CURRENT_CORE = true;
//Core::Interrupt::enable();
global_lock.lock();
return Guarded(global_vault);
}
void Guard::leave() {} void Guard::leave() {
bool istate = Core::Interrupt::disable();
Epilogue next;
global_lock.unlock();
while(epilogue_queue FOR_CURRENT_CORE.consume(next)){
Core::Interrupt::enable();
global_lock.lock();
next(global_vault);
global_lock.unlock();
Core::Interrupt::disable();
}
epi_flag FOR_CURRENT_CORE = false;
Core::Interrupt::restore(istate);
}
void Guard::relay(Epilogue handler) {
//if(!epilogue_queue FOR_CURRENT_CORE.produce(handler))
// return; // enqueue, but dont execute
//if(!epi_flag FOR_CURRENT_CORE){
// enter();
// Core::Interrupt::enable();
// handler(global_vault);
// leave();
//}
void Guard::relay(Epilogue handler) { (void)handler; } Core::Interrupt::enable(); // goto level 0.5
if(epi_flag FOR_CURRENT_CORE){
epilogue_queue->produce(handler);
}
else{
epi_flag FOR_CURRENT_CORE = true;
global_lock.lock();
handler(global_vault);
leave();
}
//epilogue_queue->consume(handler);
}
const Vault &Guard::unsafeConstAccess() { return global_vault; } const Vault &Guard::unsafeConstAccess() { return global_vault; }

View File

@@ -2,15 +2,19 @@
* \brief \ref Guard synchronizes access to epilogue level * \brief \ref Guard synchronizes access to epilogue level
*/ */
#pragma once //#pragma once
#include "../object/bbuffer.h" #include "../object/bbuffer.h"
#include "../object/key.h" #include "../object/key.h"
#include "../types.h" #include "../types.h"
#include "epilogues.h" #include "epilogues.h"
#include "../device/textstream.h"
#include "../thread/scheduler.h"
//! \brief The epilogue vault contains the protected data for the epilogue level //! \brief The epilogue vault contains the protected data for the epilogue level
struct Vault { struct Vault {
Vault(); Vault();
TextStream kout = TextStream(0, 80, 0, 10, true);
Scheduler sch = Scheduler();
// no copy // no copy
Vault(const Vault&) = delete; Vault(const Vault&) = delete;
Vault& operator=(const Vault&) = delete; Vault& operator=(const Vault&) = delete;

View File

@@ -11,4 +11,26 @@ handle_keyboard_asm:
; continue. The C++ compiler will only generates code to preserve ; continue. The C++ compiler will only generates code to preserve
; non-scratch registers in the high-level interrupt handler -- the scratch ; non-scratch registers in the high-level interrupt handler -- the scratch
; registers have to be saved (and restored later) manually! ; registers have to be saved (and restored later) manually!
; TODO(12): Implement the context save and restore for the keyboard interrupt ; TODO: Implement the context save and restore for the keyboard interrupt
;
;
push rax;
push rdi;
push rsi;
push rdx;
push rcx;
push r8;
push r9;
push r10;
push r11;
call handle_keyboard;
pop r11;
pop r10;
pop r9;
pop r8;
pop rcx;
pop rdx;
pop rsi;
pop rdi;
pop rax;
iretq ;

View File

@@ -1,4 +1,4 @@
#include "handlers.h" #include "./handlers.h"
#include "../arch/core_cr.h" #include "../arch/core_cr.h"
#include "../arch/idt.h" #include "../arch/idt.h"
@@ -7,6 +7,14 @@
#include "../debug/kernelpanic.h" #include "../debug/kernelpanic.h"
#include "../debug/output.h" #include "../debug/output.h"
#include "../device/ps2controller.h"
#include "../sync/ticketlock.h"
#include "epilogues.h"
#include "guard.h"
Key kout_key = Key();
void printContext(const InterruptContext *context) { void printContext(const InterruptContext *context) {
DBG << "ip: " << hex << context->cs << ':' << context->ip DBG << "ip: " << hex << context->cs << ':' << context->ip
<< " sp: " << context->ss << ':' << context->sp << " flags" << bin << " sp: " << context->ss << ':' << context->sp << " flags" << bin
@@ -21,8 +29,7 @@ void printContext(const InterruptContext *context) {
[[gnu::interrupt]] void handle_double_fault(InterruptContext *context, [[gnu::interrupt]] void handle_double_fault(InterruptContext *context,
uint64_t error) { uint64_t error) {
(void)error; DBG << "Double fault encountered. Error code: " << dec << error << endl;
DBG << "Double fault encoutered" << endl;
printContext(context); printContext(context);
kernelpanic("Double fault!"); kernelpanic("Double fault!");
} }
@@ -53,8 +60,7 @@ enum PAGE_FAULT_ERROR {
[[gnu::interrupt]] void handle_page_fault(InterruptContext *context, [[gnu::interrupt]] void handle_page_fault(InterruptContext *context,
uint64_t error) { uint64_t error) {
(void)error; DBG << "Page fault encountered at linear address " << hex
DBG << "Page fault encoutered at linear address " << hex
<< Core::CR<2>::read() << endl << Core::CR<2>::read() << endl
<< (error & PF_ERR_PRESENT ? "present" : "non-present") << " page|" << (error & PF_ERR_PRESENT ? "present" : "non-present") << " page|"
<< (error & PF_ERR_WRITE ? "write" : "read") << " access|" << (error & PF_ERR_WRITE ? "write" : "read") << " access|"
@@ -65,18 +71,34 @@ enum PAGE_FAULT_ERROR {
kernelpanic("Page fault!"); kernelpanic("Page fault!");
} }
void handle_keyboard() {} void handle_keyboard() {
//Key key = Key();
if (PS2Controller::fetch(kout_key)) {
LAPIC::endOfInterrupt();
if (kout_key.ctrl() && kout_key.alt() && kout_key.scancode == Key::KEY_DEL)
System::reboot();
Guard::relay(Epilogues::keyboard);
}
else
LAPIC::endOfInterrupt();
}
[[gnu::interrupt]] void handle_panic(InterruptContext *context) { [[gnu::interrupt]] void handle_panic(InterruptContext *context) {
(void)context; DBG << "Generic KernelPanic triggered"<< endl;
printContext(context);
kernelpanic("Generic Panic Triggerd");
} }
[[gnu::interrupt]] void handle_timer(InterruptContext *context) { [[gnu::interrupt]] void handle_timer(InterruptContext *context) {
(void)context; (void)context;
LAPIC::endOfInterrupt();
Guard::relay(Epilogues::timer);
} }
[[gnu::interrupt]] void handle_assassin(InterruptContext *context) { [[gnu::interrupt]] void handle_assassin(InterruptContext *context) {
(void)context; (void)context;
LAPIC::endOfInterrupt();
Guard::relay(Epilogues::assassin);
} }
[[gnu::interrupt]] void handle_wakeup(InterruptContext *context) { [[gnu::interrupt]] void handle_wakeup(InterruptContext *context) {
(void)context; (void)context;
@@ -96,7 +118,16 @@ void initInterruptHandlers() {
IDT::set(Core::Interrupt::Vector::PAGE_FAULT, IDT::set(Core::Interrupt::Vector::PAGE_FAULT,
IDT::InterruptDescriptor::ReturningWithError(handle_page_fault)); IDT::InterruptDescriptor::ReturningWithError(handle_page_fault));
IDT::set(Core::Interrupt::Vector::TIMER,
IDT::InterruptDescriptor::Returning(handle_timer));
// TODO: Add more handlers here // TODO: Add more handlers here
// Load the idt pointer IDT::set(Core::Interrupt::Vector::KEYBOARD,
IDT::InterruptDescriptor::Returning(handle_keyboard_asm));
IDT::set(Core::Interrupt::Vector::TIMER,
IDT::InterruptDescriptor::Returning(handle_timer));
IDT::set(Core::Interrupt::Vector::ASS,
IDT::InterruptDescriptor::Returning(handle_assassin));
// Load the idt pointer
IDT::load(); IDT::load();
} }

View File

@@ -65,7 +65,7 @@ extern "C" { // disable C++ name mangling for asm function
* On keyboard interrupt, the register state is saved to and restored from the * On keyboard interrupt, the register state is saved to and restored from the
* stack. This function wraps the handle_keyboard C-function. * stack. This function wraps the handle_keyboard C-function.
* *
* \todo(12) Implement in assembly * \todo Implement in assembly
*/ */
[[gnu::interrupt]] void handle_keyboard_asm(InterruptContext *context); [[gnu::interrupt]] void handle_keyboard_asm(InterruptContext *context);
@@ -77,12 +77,12 @@ extern "C" { // disable C++ name mangling for asm function
* \todo(12) Fetch a single key * \todo(12) Fetch a single key
* \todo(13) Extend to use the Prologue-Epilogue pattern * \todo(13) Extend to use the Prologue-Epilogue pattern
*/ */
void handle_keyboard(); void handle_keyboard();
} }
/*! \brief handle_panic /*! \brief handle_panic
* *
* \todo(12) Trigger a kernel panic * \todo Trigger a kernel panic
*/ */
[[gnu::interrupt]] void handle_panic(InterruptContext *context); [[gnu::interrupt]] void handle_panic(InterruptContext *context);

200
main.cc
View File

@@ -1,78 +1,155 @@
#include "arch/lapic.h" #include "arch/lapic.h"
#include "boot/startup_ap.h" #include "boot/startup_ap.h"
#include "arch/core_interrupt.h"
#include "debug/copystream.h"
#include "debug/output.h" #include "debug/output.h"
#include "debug/assert.h"
#include "arch/cga.h" #include "arch/cga.h"
#include "arch/textwindow.h" #include "arch/textwindow.h"
#include "arch/serial.h" #include "arch/serial.h"
#include "device/serialstream.h" #include "device/serialstream.h"
#include "device/textstream.h"
#include "device/ps2controller.h"
#include "arch/ioapic.h"
#include "thread/scheduler.h"
#include "user/app1/appl.h"
#include "sync/ticketlock.h"
#include "interrupt/guard.h"
#include "arch/context.h"
#include "thread/thread.h"
///TextStream kout = TextStream(0, 80, 0, 10, true);
Ticketlock koutlock;
//Scheduler sch;
//TextStream dout[8] = {
// TextStream(0 ,20,12,25,false),
// TextStream(20,40,12,25,false),
// TextStream(40,60,12,25,false),
// TextStream(60,80,12,25,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
// TextStream(0 ,0 ,0, 0,false),
//};
TextStream dout[Core::MAX] = {
{0, 40, 10, 14},
{40, 80, 10, 14},
{0, 40, 14, 18},
{40, 80, 14, 18},
{0, 40, 18, 22},
{40, 80, 18, 22},
{0, 40, 22, 25},
{40, 80, 22, 25},
};
CopyStream copystream[Core::MAX]{
{&dout[0], &sout},
{&dout[1], &sout},
{&dout[2], &sout},
{&dout[3], &sout},
{&dout[4], &sout},
{&dout[5], &sout},
{&dout[6], &sout},
{&dout[7], &sout},
};
OutputStream* copyout[Core::MAX]{
&dout[0],
&copystream[1],
&dout[2],
&dout[3],
&dout[4],
&dout[5],
&dout[6],
&dout[7]
};
unsigned int testx, testy;
uint8_t test1_stack[1024];
uint8_t test2_stack[1024];
uint8_t test3_stack[1024];
uint8_t test4_stack[1024];
uint8_t test5_stack[1024];
uint8_t test6_stack[1024];
uint8_t test7_stack[1024];
uint8_t test8_stack[1024];
uint8_t test9_stack[1024];
Application application1 = Application(&test1_stack[sizeof(test1_stack)-1]);
Application application2 = Application(&test2_stack[sizeof(test2_stack)-1]);
Application application3 = Application(&test3_stack[sizeof(test3_stack)-1]);
Application application4 = Application(&test4_stack[sizeof(test4_stack)-1]);
Application application5 = Application(&test5_stack[sizeof(test5_stack)-1]);
Application application6 = Application(&test6_stack[sizeof(test5_stack)-1]);
Application application7 = Application(&test7_stack[sizeof(test5_stack)-1]);
Application application8 = Application(&test8_stack[sizeof(test5_stack)-1]);
Application application9 = Application(&test9_stack[sizeof(test5_stack)-1]);
// Main function // Main function
// (the bootstrap processor starts here)} // (the bootstrap processor starts here)}
extern "C" int main() { extern "C" int main() {
CGA::setCursor(0, 0);
unsigned int numCPUs = Core::count(); unsigned int numCPUs = Core::count();
DBG_VERBOSE << "Number of CPUs: " << numCPUs << endl;
////test cga implemantation
//CGA::setCursor(1, 2);
//unsigned x,y;
//CGA::getCursor(x, y);
//CGA::setCursor(x+1, y+1);
//for(uint8_t i = 0; i < 10; i++)
// CGA::show(i, i, i+0x30, CGA::Attribute());
////test textwindow implemantation
//TextWindow tw_global = TextWindow(0, 80, 0, 25, true);
//tw_global.reset(' ', CGA::Attribute(CGA::LIGHT_GREEN, CGA::BLUE, false));
//TextWindow tw = TextWindow(0, 10, 0, 10, true);
//tw.reset();
//tw.setPos(0,0);
//tw.print("lorem ipsum dolor sit amit", 26);
//tw.setPos(0,-1);
//tw.print("test", 4, CGA::Attribute(CGA::BLACK, CGA::BLUE));
//int x,y;
//tw.getPos(x,y);
//tw.setPos(x+1,y);
////test Serial
//Serial s = Serial();
//s.write('a');
// test SerialStream
SerialStream ss = SerialStream();
ss.print("test", 4);
ss.setAttribute(SerialStream::UNDERSCORE);
ss.print("test", 4);
ss.setAttribute(SerialStream::RESET);
ss.setForeground(SerialStream::MAGENTA);
ss.print("test", 4);
ss.setBackground(SerialStream::CYAN);
ss.print("test", 4);
ss.setPos(10, 10);
ss.print("test\n", 5);
ss.setBackground(SerialStream::BLACK);
kout << "Test <stream result> -> <expected>" << endl;
kout << "bool: " << true << " -> true" << endl;
kout << "zero: " << 0 << " -> 0" << endl;
kout << "binary: " << bin << 42 << dec << " -> 0b101010" << endl;
kout << "octal: " << oct << 42 << dec << " -> 052" << endl;
kout << "hex: " << hex << 42 << dec << " -> 0x2a" << endl;
kout << "uint64_t max: " << ~((uint64_t)0) << " -> 18446744073709551615" << endl;
kout << "int64_t max: " << ~(1ll<<63) << " -> 9223372036854775807" << endl;
kout << "int64_t min: " << (1ll<<63) << " -> -9223372036854775808" << endl;
kout << "some int64_t: " << (-1234567890123456789) << " -> -1234567890123456789" << endl;
kout << "some int64_t: " << (1234567890123456789) << " -> 1234567890123456789" << endl;
kout << "pointer: " << reinterpret_cast<void*>(1994473406541717165ull)
<< " -> 0x1badcafefee1dead" << endl;
kout << "smiley: " << static_cast<char>(1) << endl;
/* Start application processors /* Start application processors
* To avoid unexpected behaviour, make sure that interrupts are not * To avoid unexpected behaviour, make sure that interrupts are not
* enabled before the APs are booted. Otherwise it might interfere with the * enabled before the APs are booted. Otherwise it might interfere with the
* Startup IPIs or even block devices like keyboard because of a missing EOI * Startup IPIs or even block devices like keyboard because of a missing EOI
*/ */
{
Guarded g = Guard::enter();
Scheduler &sch =g.vault().sch;
sch.ready(&application1);
sch.ready(&application2);
sch.ready(&application3);
sch.ready(&application4);
sch.ready(&application5);
sch.ready(&application6);
sch.ready(&application7);
sch.ready(&application8);
sch.ready(&application9);
}
LAPIC::Timer::setup(1000000);
ApplicationProcessor::boot(); ApplicationProcessor::boot();
PS2Controller::init();
IOAPIC::init();
IOAPIC::config(1, Core::Interrupt::KEYBOARD);
IOAPIC::allow(1);
Core::Interrupt::enable();
LAPIC::Timer::activate();
PS2Controller::drainBuffer();
DBG << "Main CPU " << static_cast<int>(LAPIC::getID()) << endl << flush;
{
Guarded g = Guard::enter();
g.vault().sch.schedule();
}
//Application{}.action();
while (true){
//DBG << "pos: " << testx << ", " << testy << endl << flush;
//Core::pause();
}
return 0; return 0;
} }
@@ -81,6 +158,17 @@ extern "C" int main() {
extern "C" int main_ap() { extern "C" int main_ap() {
DBG_VERBOSE << "CPU core " << static_cast<int>(Core::getID()) << " / LAPIC " DBG_VERBOSE << "CPU core " << static_cast<int>(Core::getID()) << " / LAPIC "
<< static_cast<int>(LAPIC::getID()) << " in main_ap()" << endl; << static_cast<int>(LAPIC::getID()) << " in main_ap()" << endl;
Core::Interrupt::enable();
DBG << "App CPU " << static_cast<int>(Core::getID()) << endl << flush;
{
Guarded g = Guard::enter();
g.vault().sch.schedule();
}
//assert(Core::getID() != 1);
//Application{}.action();
return 0; return 0;
} }

4
notes Normal file
View File

@@ -0,0 +1,4 @@
scheduler in vault
next implementieren
thread kill nicht in die liste enqueuen, scheduler.cc zeile 26
setzen von epi flags interrupt vermutlich disablen.

View File

@@ -11,39 +11,16 @@
/*! \brief Templated Queue for arbitrary objects. /*! \brief Templated Queue for arbitrary objects.
* *
* Queue is implemented by a head-object (Queue<T>) and next-pointers embedded * Queue is implemented by a head-object (Queue<T>) and next-pointers embedded
* in the queued objects. This Queue supports arrays of next-pointers by passing * in the queued objects. By passing a different get_link function into the
* an index into the constructor identifying the index into the next-pointer * constructor, the member name of the next-pointer can be changed and objects
* array. By passing a different get_link function into the constructor, the * can be contained in different independent queues.
* member name of the next-pointer array can be changed and objects can be
* contained in different independent queues.
*/ */
template <class T> template <class T>
class Queue { class Queue {
/*! \brief Default get_link implementation returns a pointer to the /// Default get_link implementation returns a pointer to the next-pointer.
* link_index'th element of the next-pointer array. static T** default_get_link(T& obj) { return &obj.queue_link; }
* The function assumes a member named "queue_link" that stores the
* next-pointer.
*
* If your object contains a queue_link member you can just ignore this
* function and the get_link keyword argument of the constructor.
*
* \param[in] obj the object whose link should be accessed.
* \param[in] link_index the index within the array.
*
* \return A pointer to the next-object pointer.
*/
static T** default_get_link(T& obj, unsigned link_index) {
assert(link_index < sizeof(T::queue_link) / sizeof(void*));
return &obj.queue_link[link_index];
}
/// Type definition for the get_link function /// Type definition for the get_link function
typedef T** (*NextFunc)(T&, unsigned); typedef T** (*NextFunc)(T&);
/// Queue-local index into the next-pointer array
unsigned link_index;
/// Provides the same signature for single- and multi-core Queue
T** get_link_wrapped(T& obj) { return get_link(obj, link_index); }
/// Function pointer to the get_link function, called whenever the /// Function pointer to the get_link function, called whenever the
/// next pointer array is referenced /// next pointer array is referenced
@@ -106,18 +83,15 @@ class Queue {
constexpr Queue(Queue&&) = default; constexpr Queue(Queue&&) = default;
/*! \brief Constructor /*! \brief Constructor
* \param[in] link_index denotes the index into the next-pointer array
* to be used by this
*queue-object
* \param[in] get_link A function pointer to the get_link, i.e. a function * \param[in] get_link A function pointer to the get_link, i.e. a function
* which returns a pointer to the * which returns a pointer to the
*next-pointer of an element in the Queue. *next-pointer of an element in the Queue.
*/ */
explicit Queue(unsigned link_index, NextFunc get_link = default_get_link) explicit Queue(NextFunc get_link = default_get_link)
: link_index(link_index), : get_link(get_link), head(nullptr), tail(nullptr) {
get_link(get_link), assert(get_link != nullptr &&
head(nullptr), "get_link function pointer must not be nullptr!");
tail(nullptr) {} };
/*! \brief Enqueues the provided item at the end of the queue. If the element /*! \brief Enqueues the provided item at the end of the queue. If the element
* is already contained in the queue, false will be returned * is already contained in the queue, false will be returned
@@ -126,7 +100,7 @@ class Queue {
* or not (and it is now enqueued, then true) * or not (and it is now enqueued, then true)
*/ */
bool enqueue(T& item) { bool enqueue(T& item) {
T** nextptr = get_link_wrapped(item); T** nextptr = get_link(item);
if (*nextptr != nullptr || (head != nullptr && tail == &item)) { if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
return false; return false;
} }
@@ -136,7 +110,7 @@ class Queue {
head = tail = &item; head = tail = &item;
} else { } else {
assert(tail != nullptr); assert(tail != nullptr);
*get_link_wrapped(*tail) = &item; *get_link(*tail) = &item;
tail = &item; tail = &item;
} }
return true; return true;
@@ -147,7 +121,7 @@ class Queue {
* \return true if successful, false if item was already in the queue * \return true if successful, false if item was already in the queue
**/ **/
bool insertFirst(T& item) { bool insertFirst(T& item) {
T** nextptr = get_link_wrapped(item); T** nextptr = get_link(item);
if (*nextptr != nullptr || (head != nullptr && tail == &item)) { if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
return false; return false;
} }
@@ -177,13 +151,13 @@ class Queue {
if (&after == tail) { if (&after == tail) {
return enqueue(item); return enqueue(item);
} }
T** nextptr = get_link_wrapped(item); T** nextptr = get_link(item);
// if item is already in the list return false // if item is already in the list return false
if (*nextptr != nullptr || tail == &item) { if (*nextptr != nullptr || tail == &item) {
return false; return false;
} }
T** pnextptr = get_link_wrapped(after); T** pnextptr = get_link(after);
// if after is NOT in the list, return false // if after is NOT in the list, return false
if (!(pnextptr != nullptr || tail == &after)) { if (!(pnextptr != nullptr || tail == &after)) {
return false; return false;
@@ -201,8 +175,7 @@ class Queue {
*not in this list *not in this list
**/ **/
T* next(T& item) { T* next(T& item) {
T** nextptr = get_link_wrapped(item); T** nextptr = get_link(item);
// if item is already in the list return nullptr
if (head == nullptr || (*nextptr == nullptr && tail != &item)) { if (head == nullptr || (*nextptr == nullptr && tail != &item)) {
return nullptr; return nullptr;
} }
@@ -222,7 +195,7 @@ class Queue {
T* dequeue() { T* dequeue() {
T* out = head; T* out = head;
if (head != nullptr) { if (head != nullptr) {
T** nextptr = get_link_wrapped(*head); T** nextptr = get_link(*head);
head = *nextptr; head = *nextptr;
*nextptr = nullptr; *nextptr = nullptr;
} }
@@ -239,21 +212,21 @@ class Queue {
T** next_link; T** next_link;
if (head == that) { if (head == that) {
head = *get_link_wrapped(*head); head = *get_link(*head);
*get_link_wrapped(*that) = nullptr; *get_link(*that) = nullptr;
return that; return that;
} }
while (cur) { while (cur) {
next_link = get_link_wrapped(*cur); next_link = get_link(*cur);
if (*next_link == that) { if (*next_link == that) {
*next_link = *get_link_wrapped(**next_link); *next_link = *get_link(**next_link);
if (that == tail) { if (that == tail) {
tail = cur; tail = cur;
} }
*get_link_wrapped(*that) = nullptr; *get_link(*that) = nullptr;
return that; return that;
} }
cur = *next_link; cur = *next_link;

View File

@@ -52,7 +52,6 @@ class Stringbuffer {
* *
* \param c Char to be added * \param c Char to be added
* *
* \todo(11) Implement
*/ */
void put(char c); void put(char c);

View File

@@ -1,19 +1,6 @@
#include "bellringer.h" #include "./bellringer.h"
#include "../interrupt/guard.h" #include "../interrupt/guard.h"
#include "../thread/thread.h"
struct Bell {
// link pointer to the next bell in the bellringers bell list
Bell *queue_link[1] = {nullptr};
Thread *thread;
size_t counter;
};
Bell **Bellringer::bell_link(Bell &obj, unsigned link_index) {
return &obj.queue_link[link_index];
}
// check: Checks whether bells are running out of time and rings them if // check: Checks whether bells are running out of time and rings them if
// necessary // necessary

View File

@@ -4,10 +4,10 @@
#pragma once #pragma once
#include "../object/queue.h" #include "../object/queue.h"
#include "../thread/thread.h"
#include "../types.h" #include "../types.h"
struct Vault; struct Vault;
struct Bell;
/*! \brief Manages and activates time-triggered activities. /*! \brief Manages and activates time-triggered activities.
* \ingroup ipc * \ingroup ipc
@@ -24,6 +24,14 @@ class Bellringer {
Bellringer(const Bellringer&) = delete; Bellringer(const Bellringer&) = delete;
Bellringer& operator=(const Bellringer&) = delete; Bellringer& operator=(const Bellringer&) = delete;
struct Bell {
// link pointer to the next bell in the bellringers bell list
Bell* queue_link = nullptr;
Thread* thread;
size_t counter;
};
/*! \brief List of bells currently managed. /*! \brief List of bells currently managed.
* *
* This list contains non-expired bells enqueued by job(). * This list contains non-expired bells enqueued by job().
@@ -34,26 +42,26 @@ class Bellringer {
*/ */
Queue<Bell> bells; Queue<Bell> bells;
//! Link pointer for bells
static Bell** bell_link(Bell& obj, unsigned link_index);
public: public:
// constructor // constructor
Bellringer() : bells(0, bell_link) {} Bellringer() : bells() {}
/*! \brief Checks whether there are bells to be rung. /*! \brief Checks whether there are bells to be rung.
* *
* Every call to check elapses a tick. Once such a tick reduces a bells * Every call to check elapses a tick. Once such a tick reduces a bells
* remaining time to zero, the bell will be rung. * remaining time to zero, the bell will be rung.
* *
* \param vault The vault containing the bellringer instance
*
* \todo(16) Implement Method * \todo(16) Implement Method
*/ */
void check(Vault& vault); void check(Vault& vault);
/*! \brief Passes a `bell` to the bellringer to be rung after `ms` /*! \brief Passes a `bell` to the bellringer to be rung after `ms`
* milliseconds. * milliseconds.
* \param bell Bell that should be rung after `ms` milliseconds *
* \param ms number of milliseconds that should be waited before * \param vault The vault containing the bellringer instance
* \param ms Number of milliseconds that should be waited before
* ringing the bell * ringing the bell
* *
* \todo(16) Implement Method * \todo(16) Implement Method

View File

@@ -1,16 +1,7 @@
#include "./semaphore.h" #include "./semaphore.h"
#include "../interrupt/guard.h"
#include "../thread/thread.h"
Semaphore::Semaphore(unsigned c) { (void)c; } Semaphore::Semaphore(unsigned c) { (void)c; }
Thread **Semaphore::thread_link(Thread &obj, unsigned link_index) {
(void)obj;
(void)link_index;
return nullptr;
}
void Semaphore::p(Vault &vault) { (void)vault; } void Semaphore::p(Vault &vault) { (void)vault; }
void Semaphore::v(Vault &vault) { (void)vault; } void Semaphore::v(Vault &vault) { (void)vault; }

View File

@@ -26,8 +26,6 @@ class Semaphore {
Semaphore(const Semaphore&) = delete; Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete; Semaphore& operator=(const Semaphore&) = delete;
static Thread** thread_link(Thread& obj, unsigned link_index);
public: public:
/*! \brief Constructor; initialized the counter with provided value `c` /*! \brief Constructor; initialized the counter with provided value `c`
* \param c Initial counter value * \param c Initial counter value

View File

@@ -35,14 +35,15 @@ class Spinlock {
// Prevent copies and assignments // Prevent copies and assignments
Spinlock(const Spinlock& copy) = delete; Spinlock(const Spinlock& copy) = delete;
Spinlock& operator=(const Spinlock&) = delete; Spinlock& operator=(const Spinlock&) = delete;
private:
volatile uint64_t locked;
public: public:
/*! \brief Constructor; Initializes as unlocked. /*! \brief Constructor; Initializes as unlocked.
* *
* \todo(12) Complete Constructor (for \MPStuBS, or use \ref Ticketlock) * \todo(12) Complete Constructor (for \MPStuBS, or use \ref Ticketlock)
* *
*/ */
consteval Spinlock() {} consteval Spinlock() : locked(0) {}
/*! \brief Enters the critical area. In case the area is already locked, /*! \brief Enters the critical area. In case the area is already locked,
* \ref lock() will actively wait until the area can be entered. * \ref lock() will actively wait until the area can be entered.
@@ -50,11 +51,11 @@ class Spinlock {
* \see \ref Core::pause() * \see \ref Core::pause()
* \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock) * \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock)
*/ */
void lock() {} void lock();
/*! \brief Unblocks the critical area. /*! \brief Unblocks the critical area.
* *
* \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock) * \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock)
*/ */
void unlock() {} void unlock();
}; };

15
sync/ticketlock.cc Normal file
View File

@@ -0,0 +1,15 @@
#include "sync/ticketlock.h"
#include "debug/output.h"
void Ticketlock::lock() {
uint64_t ticket = __atomic_fetch_add(&ticket_count, 1, __ATOMIC_RELAXED);
while (ticket != __atomic_fetch_add(&ticket_current, 0, __ATOMIC_ACQUIRE)) {
Core::pause();
}
}
void Ticketlock::unlock() {
if(ticket_current != ticket_count){
__atomic_fetch_add(&ticket_current, 1, __ATOMIC_RELEASE);
}
}

View File

@@ -3,7 +3,10 @@
*/ */
#pragma once #pragma once
#include "../types.h"
#include "../arch/core.h"
#include "../arch/cache.h"
/*! \brief Using Ticketlocks, it is possible to serialize blocks of code /*! \brief Using Ticketlocks, it is possible to serialize blocks of code
* that might otherwise run in parallel on multiple CPU cores, * that might otherwise run in parallel on multiple CPU cores,
* or be interleaved due to interrupts or scheduling. * or be interleaved due to interrupts or scheduling.
@@ -29,12 +32,17 @@ class Ticketlock {
Ticketlock(const Ticketlock& copy) = delete; Ticketlock(const Ticketlock& copy) = delete;
Ticketlock& operator=(const Ticketlock&) = delete; Ticketlock& operator=(const Ticketlock&) = delete;
private:
volatile uint64_t ticket_current;
volatile uint64_t ticket_count;
public: public:
/*! \brief Constructor /*! \brief Constructor
* *
* \todo(12) Complete Constructor (for \MPStuBS) * \todo(12) Complete Constructor (for \MPStuBS)
*/ */
consteval Ticketlock() {} consteval Ticketlock() : ticket_current(0), ticket_count(0) {}
/*! \brief Enters the critical area. In case the area is already locked, /*! \brief Enters the critical area. In case the area is already locked,
* \ref lock() will actively wait until the area can be entered. * \ref lock() will actively wait until the area can be entered.
@@ -42,11 +50,11 @@ class Ticketlock {
* \see \ref Core::pause() * \see \ref Core::pause()
* \todo(12) Implement Method (for \MPStuBS) * \todo(12) Implement Method (for \MPStuBS)
*/ */
void lock() {} void lock();
/*! \brief Unblocks the critical area. /*! \brief Unblocks the critical area.
* *
* \todo(12) Implement Method (for \MPStuBS) * \todo(12) Implement Method (for \MPStuBS)
*/ */
void unlock() {} void unlock();
}; };

View File

@@ -1,17 +1,32 @@
// vim: set noet ts=4 sw=4: // vim: set noet ts=4 sw=4:
#include "dispatcher.h" #include "dispatcher.h"
#include "../arch/core.h"
Dispatcher::Dispatcher() {} Dispatcher::Dispatcher() {
Thread *Dispatcher::active() { return nullptr; }
bool Dispatcher::isActive(const Thread *thread, unsigned *cpu) {
(void)thread;
(void)cpu;
return false;
} }
void Dispatcher::go(Thread *first) { (void)first; } Thread *Dispatcher::active() {
return lifePointer[Core::getID()];
}
void Dispatcher::dispatch(Thread *next) { (void)next; } bool Dispatcher::isActive(const Thread *thread, unsigned *cpu) {
for(uint8_t i=0; i<Core::MAX; i++){
if(thread == lifePointer[i]){
*cpu = i;
return true;
}
}
cpu = nullptr;
return false;
}
void Dispatcher::go(Thread *first) {
lifePointer[Core::getID()] = first;
first->go();
}
void Dispatcher::dispatch(Thread *next) {
lifePointer[Core::getID()] = next;
lifePointer[Core::getID()]->resume(next);
}

View File

@@ -6,6 +6,7 @@
#pragma once #pragma once
#include "../thread/thread.h" #include "../thread/thread.h"
#include "../types.h" #include "../types.h"
#include "../arch/core.h"
/*! \brief The dispatcher dispatches threads and puts the scheduler's /*! \brief The dispatcher dispatches threads and puts the scheduler's
* decisions into action. * decisions into action.
@@ -23,12 +24,14 @@ class Dispatcher {
*/ */
void setActive(Thread* thread) { (void)thread; } void setActive(Thread* thread) { (void)thread; }
public: public:
/*! \brief constructor /*! \brief constructor
* *
* \todo(14) Implement Method * \todo(14) Implement Method
*/ */
Dispatcher(); Dispatcher();
Thread* lifePointer[Core::MAX];
/*! \brief Returns the thread currently running on the CPU core calling /*! \brief Returns the thread currently running on the CPU core calling
* this method * this method

View File

@@ -2,22 +2,47 @@
#include "scheduler.h" #include "scheduler.h"
Scheduler::Scheduler() {} Queue<Thread> readyList = Queue<Thread>();
Scheduler::Scheduler() {
}
Thread* Scheduler::getNext() { return nullptr; } Thread* Scheduler::getNext() { return nullptr; }
void Scheduler::schedule() {} void Scheduler::schedule() {
// TODO maybe guard?
Thread* next = readyList.dequeue();
assert(next != nullptr);
dispatcher.go(next);
}
void Scheduler::ready(Thread* that) { (void)that; } void Scheduler::ready(Thread* that) {
assert(that != nullptr);
readyList.enqueue(*that);
}
void Scheduler::resume(bool ready) { (void)ready; } void Scheduler::resume(bool ready) {
//if (!active()->kill_flag)
if (ready)
readyList.enqueue(*active());
Thread* thread = readyList.dequeue();
assert(thread != nullptr);
dispatcher.dispatch(thread);
}
void Scheduler::exit() {} void Scheduler::exit() {
Thread* thread = readyList.dequeue();
assert(thread != nullptr);
dispatcher.dispatch(thread);
}
void Scheduler::kill(Thread* that) { (void)that; } void Scheduler::kill(Thread* that) {
readyList.remove(that);
that->kill_flag = true;
}
bool Scheduler::isActive(const Thread* that, unsigned int* cpu) { bool Scheduler::isActive(const Thread* thread, unsigned int* cpu) {
(void)that; (void)thread;
(void)cpu; (void)cpu;
return false; return false;
} }

View File

@@ -22,18 +22,20 @@
* taken from the front of the queue. * taken from the front of the queue.
*/ */
class Scheduler { class Scheduler {
/*! \brief Helper to retrieve next Thread
* \return pointer of next thread
*/
Thread* getNext();
public:
Scheduler();
/*! \brief a Dispatcher object, providing the low level context switching /*! \brief a Dispatcher object, providing the low level context switching
* routines. * routines.
*/ */
Dispatcher dispatcher; Dispatcher dispatcher;
/*! \brief Helper to retrieve next Thread
* \return pointer of next thread
*/
Thread* getNext();
public:
Scheduler();
/*! \brief Start scheduling /*! \brief Start scheduling
* *
@@ -103,7 +105,7 @@ class Scheduler {
Thread* active() { return dispatcher.active(); } Thread* active() { return dispatcher.active(); }
/// \copydoc Dispatcher::isActive /// \copydoc Dispatcher::isActive
bool isActive(const Thread* that, unsigned int* cpu = nullptr); bool isActive(const Thread* thread, unsigned int* cpu = nullptr);
/*! \brief Checks whether the ready queue is empty. /*! \brief Checks whether the ready queue is empty.
* *

View File

@@ -1,16 +1,43 @@
// vim: set noet ts=4 sw=4: // vim: set noet ts=4 sw=4:
#include "thread.h" #include "thread.h"
#include "../debug/output.h"
#include "../arch/context.h"
#include "interrupt/guard.h"
// Alias to simplify stuff // Alias to simplify stuff
typedef void (*kickoff_t)(void*); typedef void (*kickoff_t)(void*);
void Thread::kickoff(Thread* object) { (void)object; } void Thread::kickoff(Thread* object) {
Guard::leave();
object->action();
}
Thread::Thread(void* tos) { (void)tos; } Thread::Thread(void* tos) {
prepareContext(tos, context, (void(*)(void*)) &(kickoff), this);
}
void Thread::resume(Thread* next) { (void)next; } #include "../thread/scheduler.h"
void Thread::resume(Thread* next) {
Context *from;
Context *to;
void Thread::go() {}
from = &this->context;
to = &next->context;
void Thread::action() {} //dispatcher.lifePointer[Core::getID()] = next;
DBG << "from: " << hex << from << endl;
DBG << "to : " << hex << to << endl << flush;
context_switch(to, from);
}
void Thread::go() {
context_launch(&context);
}
void Thread::action() {
while(1);
}

View File

@@ -16,12 +16,11 @@
#include "../arch/context.h" #include "../arch/context.h"
#include "../object/queue.h" #include "../object/queue.h"
#include "../types.h" #include "../types.h"
/*! \brief The Thread is an object used by the scheduler. /*! \brief The Thread is an object used by the scheduler.
* \ingroup thread * \ingroup thread
*/ */
class Thread { class Thread {
protected: public:
/*! \brief The thread's entry point. /*! \brief The thread's entry point.
* *
* For the first activation of a thread, we need a "return address" * For the first activation of a thread, we need a "return address"
@@ -36,13 +35,15 @@ class Thread {
* *
* \param object Thread to be started * \param object Thread to be started
*/ */
/// \todo(14) Implement Method
static void kickoff(Thread* object); static void kickoff(Thread* object);
volatile bool kill_flag;
Context context;
Thread* queue_link;
public: public:
/*! \brief Marker for a dying thread /*! \brief Marker for a dying thread
*/ */
volatile bool kill_flag;
/*! \brief Constructor /*! \brief Constructor
* Initializes the context using \ref prepareContext with the given stack * Initializes the context using \ref prepareContext with the given stack

View File

@@ -59,7 +59,7 @@ kvm: all
# Execute Qemu with activated GDB stub and directly connect GDB to the spawned Qemu. # Execute Qemu with activated GDB stub and directly connect GDB to the spawned Qemu.
gdb: all gdb: all
${VERBOSE} cgdb -d $(GDB) "$(DBGKERNEL)" \ ${VERBOSE} cgdb "$(DBGKERNEL)" \
-ex "set arch $(DBGARCH)" \ -ex "set arch $(DBGARCH)" \
-ex "target remote | exec $(QEMU) -gdb stdio $(QEMUKERNEL) -smp $(QEMUCPUS) -S $(QEMUFLAGS) $(DBGFLAGS)" -ex "target remote | exec $(QEMU) -gdb stdio $(QEMUKERNEL) -smp $(QEMUCPUS) -S $(QEMUFLAGS) $(DBGFLAGS)"

View File

@@ -1,7 +1,7 @@
# Test your system on real hardware # Test your system on real hardware
NETBOOT_LOCAL="/ibr/adm/user-boot/" NETBOOT_LOCAL="/ibr/adm/user-boot/"
NETBOOT_HOST="x1.ibr.cs.tu-bs.de" NETBOOT_HOST="y0080589@x1.ibr.cs.tu-bs.de"
# The boot menu shows pairs of `vmlinuz-*` + `initrd-*.img` with owning user and timestamp. # The boot menu shows pairs of `vmlinuz-*` + `initrd-*.img` with owning user and timestamp.
# We just need to choose a name that doesn't overlap with another user's. # We just need to choose a name that doesn't overlap with another user's.

View File

@@ -1,6 +1,62 @@
// vim: set noet ts=4 sw=4: // vim: set noet ts=4 sw=4:
#include "appl.h" #include "appl.h"
#include "../../device/ps2controller.h"
#include "../../object/outputstream.h"
#include "../../device/textstream.h"
#include "../../sync/ticketlock.h"
#include "../../arch/core.h"
#include "../../interrupt/guard.h"
#include "../../debug/output.h"
#include "../../arch/context.h"
static uint8_t appl_cnt = 0;
char text[] = "Ich mag\n\
Saftige Pflaumen voller Aroma\n\
Ich knuddel jede Oma ins Koma\n\
Ich bin Großmutterknuddler, Großmutterknuddler\n\
Ich bin Großmutterknuddler, ich bin Großmutterknuddler\n\
\n\
Saftige Pflaumen voller Aroma\n\
Ich knuddel jede Oma ins Koma\n\
Ich bin Großmutterknuddler, Großmutterknuddler\n\
Ich bin Großmuttersniffer\n\
Und wacht sie aus'm Koma auf, kriegt sie von mir 'n Sticker\n\
\n";
extern Ticketlock koutlock;
extern Context* test2; extern Context* test1;
extern uint8_t test1_stack[], test2_stack[];
void activeWaitDelay(uint64_t cycles) {
uint64_t counter = 0; // Use volatile to prevent optimization
for (uint64_t i = 0; i < cycles; ++i) {
counter++; // Simple operation to consume cycles
asm("nop");
//Core::pause();
}
}
void Application::action() { // NOLINT void Application::action() { // NOLINT
uint16_t cnt = 0;
uint8_t row = appl_cnt++;
while (1) {
//koutlock.lock();
{
Guarded g = Guard::enter();
//g.vault();
g.vault().kout.setPos((unsigned)10*row,(unsigned)/*Core::getID()*2+*/1);
g.vault().kout << cnt++ << flush;
//g.vault().kout << endl << flush;
//Guard::leave();
//koutlock.unlock();
}
//Core::pause();
//activeWaitDelay(10000000);
if(cnt >= sizeof(text)-1)
cnt=0;
//context_switch(test2,test1);
}
} }

View File

@@ -6,25 +6,26 @@
#pragma once #pragma once
#include "../../types.h" #include "../../types.h"
#include "../../thread/thread.h"
//! \brief Test application //! \brief Test application
//! //!
//! \todo(12) Create a test application //! \todo(12) Create a test application
//! \todo(14) Application should inherit from \ref Thread //! \todo(14) Application should inherit from \ref Thread
//! \todo(16) Make some noise using the \ref PIT::pcspeaker "PC Speaker" //! \todo(16) Make some noise using the \ref PIT::pcspeaker "PC Speaker"
class Application { class Application:public Thread {
// Prevent copies and assignments // Prevent copies and assignments
Application(const Application&) = delete; Application(const Application&) = delete;
Application& operator=(const Application&) = delete; Application& operator=(const Application&) = delete;
public: public:
Application(Application&&) = default; // XXX: is this used anywhere? Application() = default; // XXX: is this used anywhere?
/*! \brief Constructor /*! \brief Constructor
* *
* \todo(14) Implement Constructor * \todo(14) Implement Constructor
*/ */
Application(void* tos): Thread(tos){
}
/*! \brief Contains the application code. /*! \brief Contains the application code.
* *
* \todo(14) Implement Method * \todo(14) Implement Method