This commit is contained in:
Lenni Hein 2025-02-20 18:50:04 +01:00
parent 5b881c5cde
commit 47c10976f0
4 changed files with 72 additions and 21 deletions

View File

@ -2,6 +2,8 @@
### Primitive
in `./src/lib.c` sind folgende Primitive:
`time_maccess` misst wie lange der Zugriff auf die Speicheradresse `addr` dauert.
```C
size_t time_maccess(void (* addr)(void))
@ -53,16 +55,36 @@ void flush(void* addr)
```bash
gcc -O0 cache_test.c primitive.c -o cache_test
```
##### [Muster](./src/cache_test.c)
- CMake ist praktisch! siehe `./src/CMakeList.txt`. Verwenden durch `cmake .` und dann `make`.
Muster: `./src/cache_test.c`
### Block 2: Signale über den Cache
`sharedlib.c` sieht so aus:
```C
...
void function(void)
{ asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
...
```
Das kann man so kompilieren:
Die Adresse von `function` kann also zum Flushen und Reloaded verwendet werden.
Da immer eine gesamte Cacheline geladen wird, sollte sich nichts anderes in dem selben 64Byte Block befinden.
Die Funktionen `padding_before` und `padding_after` sind beide 64 Byte groß, also eine Cacheline.
Damit kann dann auch gar nichts mehr schief gehen :)
`sharedlib.c` kann man so kompilieren und linken:
```bash
# sharedlib kompilieren
@ -73,9 +95,11 @@ gcc -O0 empfaenger.c primitive.c -L. -lsharedlib -o empfaenger
# ggfs. muss man noch den PATH exportieren
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
```
dann kann man `function` verwenden:
```C
# in e.g. sender.c
maccess((void *) function)
...
```
@ -83,7 +107,12 @@ maccess((void *) function)
- `taskset` zum pinnen der Programme auf einen bestimmten CPU-Kern.
- Verwendet man blockierenden Sleep um zu Takten, z.B. `clock_nanosleep`, dann sollte man unbedingt darauf achten nicht eine Periodendauer zu schlafen, sondern nur bis zum nächsten Zeitabschnitt. Einfacher geht es mit nicht-blockierendem Sleep, z.B. `ualarm`.
- Der Zeitabstand von Flush zu Reload sollte MAXIMIERT werden! Dies ist das Zeitfenster, in welchem der Sender die Adresse(n) zurück in den Cache laden kann.
- (Der Sender kann das Flushen übernehmen)
- (Der Sender kann im gesamten Zeitabschnitt Flushen oder Laden, nicht nur einmal)
### Block 3: Pakete über den Cache als Medium
- Kann man irgendwie mehr als ein Bit gleichzeitig senden? ;)
Muster: `./src/sender.c` und `./src/receiver.c`

View File

@ -8,8 +8,10 @@ extern void flush(void* addr);
extern void maccess(void* addr);
extern uint64_t time_maccess(void* addr);
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses) {
for (size_t i = 0; i < num_accesses; i++) {
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses)
{
for (size_t i = 0; i < num_accesses; i++)
{
flush((void*) addr);
misses[i] = time_maccess(addr);
@ -19,10 +21,12 @@ void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t*
}
}
size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses) {
size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses)
{
size_t hit_sum = 0, miss_sum = 0;
for (size_t i = 0; i < num_accesses; i++) {
for (size_t i = 0; i < num_accesses; i++)
{
hit_sum += hits[i];
miss_sum += misses[i];
}
@ -33,16 +37,18 @@ size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses) {
return (avg_hit + avg_miss) / 2; // Midpoint as threshold
}
int main() {
int main()
{
size_t* hits = malloc(NUM_ACCESSES * sizeof(size_t));
size_t* misses = malloc(NUM_ACCESSES * sizeof(size_t));
if (!hits || !misses) {
if (!hits || !misses)
{
perror("Memory allocation failed");
return 1;
}
measure_multiple_accesses((void*)maccess, NUM_ACCESSES, hits, misses);
measure_multiple_accesses((void*) maccess, NUM_ACCESSES, hits, misses);
size_t threshold = find_crossover_point(hits, misses, NUM_ACCESSES);
printf("Suggested threshold: %zu\n", threshold);

View File

@ -16,23 +16,31 @@ volatile int sample_interval = 100; // Default: 100 µs
volatile int threshold = 245; // Default: 245 cycles
void signal_handler(int signo) {
if (signo == SIGALRM) {
if (byte_index >= BUFFER_SIZE) {
if (signo == SIGALRM)
{
// Check if buffer is full
if (byte_index >= BUFFER_SIZE)
{
ualarm(0, 0); // Stop sampling
printf("Buffer full. Stopping capture.\n");
return;
}
// Receive a byte
uint8_t byte = 0;
for (int i = 0; i < BITS; i++) {
for (int i = 0; i < BITS; i++)
{
size_t time = time_maccess((void *) function + (i << 9));
if (time < threshold) {
if (time < threshold)
{
// set the ith bit of byte to 1 if it was a cache hit
byte |= (1 << i);
}
}
// flush the cache
for (int i = 0; i < BITS; i++) {
for (int i = 0; i < BITS; i++)
{
flush((void *) function + (i << 9));
}
@ -41,7 +49,8 @@ void signal_handler(int signo) {
}
int main(int argc, char *argv[]) {
if (argc < 3) {
if (argc < 3)
{
fprintf(stderr, "Usage: %s <sample_interval> <threshold>\n", argv[0]);
return 1;
}
@ -51,7 +60,9 @@ int main(int argc, char *argv[]) {
printf("Receiver started with sample interval: %d µs, threshold: %d cycles\n", sample_interval, threshold);
for (int i = 0; i < BITS; i++) {
// Flush the cache once before starting
for (int i = 0; i < BITS; i++)
{
flush((void *) function + (i << 9));
}

View File

@ -14,8 +14,10 @@ volatile size_t buffer_len = 0;
volatile size_t current_index = 0;
volatile uint8_t byte_to_send = 0;
void signal_handler(int signo) {
if (signo == SIGALRM) {
void signal_handler(int signo)
{
if (signo == SIGALRM)
{
if (current_index < buffer_len)
{
byte_to_send = buffer[current_index++];
@ -26,7 +28,8 @@ void signal_handler(int signo) {
}
}
int main(int argc, char *argv[]) {
int main(int argc, char *argv[])
{
if (argc < 3)
{
fprintf(stderr, "Usage: %s <string> <sample_interval>\n", argv[0]);
@ -37,9 +40,11 @@ int main(int argc, char *argv[]) {
buffer_len = strlen(argv[1]);
int sample_interval = atoi(argv[2]);
// set up an alarm signal that triggers every timeframe
signal(SIGALRM, signal_handler);
ualarm(sample_interval, sample_interval);
// during the timeframe, continuously send the signal
while (1)
{
for (int i = 0; i < BITS; i++)