From 89baa542ffed17dfc86b96a65309f0ecb1970c26 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 14:45:11 +0000 Subject: [PATCH 01/36] chore: add vscode C/C++ include settings --- .vscode/c_cpp_properties.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .vscode/c_cpp_properties.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..85d50b18 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [], + "compilerPath": "/usr/bin/gcc", + "intelliSenseMode": "linux-gcc-x64", + "cStandard": "c17", + "cppStandard": "c++17" + } + ], + "version": 4 +} From e7fefe3a3adac2504a3b8ba45654f07d652baced Mon Sep 17 00:00:00 2001 From: nukopy Date: Mon, 22 Sep 2025 00:12:18 +0900 Subject: [PATCH 02/36] chore: add clang-format setting file --- .clang-format | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..b68024fe --- /dev/null +++ b/.clang-format @@ -0,0 +1,246 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + From 87d101c28e949c6eb377c26abf50b693c9ebae3e Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 15:19:07 +0000 Subject: [PATCH 03/36] chore: add clang-format setting --- .vscode/settings.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..8e91d6c0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.formatOnSave": true, + "clang-format.executable": "/usr/bin/clang-format", + "[c]": { + "editor.defaultFormatter": "xaver.clang-format", + } +} From 37add5906a9cb48cd4d635ded2db9bff6aa83401 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 15:19:35 +0000 Subject: [PATCH 04/36] feat: add empty files to impl network device --- driver/dummy.c | 0 driver/dummy.h | 0 net.c | 0 net.h | 0 test/step1.c | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 driver/dummy.c create mode 100644 driver/dummy.h create mode 100644 net.c create mode 100644 net.h create mode 100644 test/step1.c diff --git a/driver/dummy.c b/driver/dummy.c new file mode 100644 index 00000000..e69de29b diff --git a/driver/dummy.h b/driver/dummy.h new file mode 100644 index 00000000..e69de29b diff --git a/net.c b/net.c new file mode 100644 index 00000000..e69de29b diff --git a/net.h b/net.h new file mode 100644 index 00000000..e69de29b diff --git a/test/step1.c b/test/step1.c new file mode 100644 index 00000000..e69de29b From 65da91d24875e1f97e3a76be8ec0f6ec60bea99e Mon Sep 17 00:00:00 2001 From: nukopy Date: Mon, 22 Sep 2025 00:25:51 +0900 Subject: [PATCH 05/36] chore: add format command --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f5f40c10..714b4578 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -APPS = +APPS = -DRIVERS = +DRIVERS = OBJS = util.o \ @@ -21,7 +21,7 @@ endif .SUFFIXES: .SUFFIXES: .c .o -.PHONY: all clean +.PHONY: all clean format all: $(APPS) $(TESTS) @@ -36,3 +36,6 @@ $(TESTS): %.exe : %.o $(OBJS) $(DRIVERS) test/test.h clean: rm -rf $(APPS) $(APPS:.exe=.o) $(OBJS) $(DRIVERS) $(TESTS) $(TESTS:.exe=.o) + +format: + find . -name "*.c" -o -name "*.h" | xargs clang-format -i --sort-includes From 5e61a1a25ec261df16624b6d2b1458effd7576a4 Mon Sep 17 00:00:00 2001 From: nukopy Date: Mon, 22 Sep 2025 00:26:05 +0900 Subject: [PATCH 06/36] chore: format all with `make format` --- platform/linux/platform.h | 32 +--- test/step0.c | 12 +- test/test.h | 26 ++- util.c | 335 ++++++++++++++++++-------------------- util.h | 96 +++++------ 5 files changed, 225 insertions(+), 276 deletions(-) diff --git a/platform/linux/platform.h b/platform/linux/platform.h index 0b8bce2c..e1dd9107 100644 --- a/platform/linux/platform.h +++ b/platform/linux/platform.h @@ -1,25 +1,17 @@ #ifndef PLATFORM_H #define PLATFORM_H +#include #include #include -#include /* * Memory */ -static inline void * -memory_alloc(size_t size) -{ - return calloc(1, size); -} +static inline void *memory_alloc(size_t size) { return calloc(1, size); } -static inline void -memory_free(void *ptr) -{ - free(ptr); -} +static inline void memory_free(void *ptr) { free(ptr); } /* * Mutex @@ -29,22 +21,16 @@ typedef pthread_mutex_t mutex_t; #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -static inline int -mutex_init(mutex_t *mutex) -{ - return pthread_mutex_init(mutex, NULL); +static inline int mutex_init(mutex_t *mutex) { + return pthread_mutex_init(mutex, NULL); } -static inline int -mutex_lock(mutex_t *mutex) -{ - return pthread_mutex_lock(mutex); +static inline int mutex_lock(mutex_t *mutex) { + return pthread_mutex_lock(mutex); } -static inline int -mutex_unlock(mutex_t *mutex) -{ - return pthread_mutex_unlock(mutex); +static inline int mutex_unlock(mutex_t *mutex) { + return pthread_mutex_unlock(mutex); } #endif diff --git a/test/step0.c b/test/step0.c index 80f28c3f..59f9f548 100644 --- a/test/step0.c +++ b/test/step0.c @@ -1,12 +1,10 @@ -#include "util.h" #include "test.h" +#include "util.h" -int -main(void) -{ - debugf("Hello, World!"); +int main(void) { + debugf("Hello, World!"); - debugdump(test_data, sizeof(test_data)); + debugdump(test_data, sizeof(test_data)); - return 0; + return 0; } diff --git a/test/test.h b/test/test.h index 0cc02a54..c8fcbafd 100644 --- a/test/test.h +++ b/test/test.h @@ -3,32 +3,26 @@ #include -/* Scope of Internet host loopback address. see https://tools.ietf.org/html/rfc5735 */ +/* Scope of Internet host loopback address. see + * https://tools.ietf.org/html/rfc5735 */ #define LOOPBACK_IP_ADDR "127.0.0.1" #define LOOPBACK_NETMASK "255.0.0.0" #define ETHER_TAP_NAME "tap0" -/* Scope of EUI-48 Documentation Values. see https://tools.ietf.org/html/rfc7042 */ +/* Scope of EUI-48 Documentation Values. see https://tools.ietf.org/html/rfc7042 + */ #define ETHER_TAP_HW_ADDR "00:00:5e:00:53:01" -/* Scope of Documentation Address Blocks (TEST-NET-1). see https://tools.ietf.org/html/rfc5731 */ +/* Scope of Documentation Address Blocks (TEST-NET-1). see + * https://tools.ietf.org/html/rfc5731 */ #define ETHER_TAP_IP_ADDR "192.0.2.2" #define ETHER_TAP_NETMASK "255.255.255.0" #define DEFAULT_GATEWAY "192.0.2.1" const uint8_t test_data[] = { - 0x45, 0x00, 0x00, 0x30, - 0x00, 0x80, 0x00, 0x00, - 0xff, 0x01, 0xbd, 0x4a, - 0x7f, 0x00, 0x00, 0x01, - 0x7f, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x35, 0x64, - 0x00, 0x80, 0x00, 0x01, - 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x37, 0x38, - 0x39, 0x30, 0x21, 0x40, - 0x23, 0x24, 0x25, 0x5e, - 0x26, 0x2a, 0x28, 0x29 -}; + 0x45, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00, 0x00, 0xff, 0x01, 0xbd, 0x4a, + 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x35, 0x64, + 0x00, 0x80, 0x00, 0x01, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x30, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26, 0x2a, 0x28, 0x29}; #endif diff --git a/util.c b/util.c index bd7697be..aba7d5ba 100644 --- a/util.c +++ b/util.c @@ -1,13 +1,13 @@ -#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include #include +#include #include "platform.h" @@ -17,61 +17,61 @@ * Logging */ -int -lprintf(FILE *fp, int level, const char *file, int line, const char *func, const char *fmt, ...) -{ - struct timeval tv; - struct tm tm; - char timestamp[32]; - int n = 0; - va_list ap; - - flockfile(fp); - gettimeofday(&tv, NULL); - strftime(timestamp, sizeof(timestamp), "%T", localtime_r(&tv.tv_sec, &tm)); - n += fprintf(fp, "%s.%03d [%c] %s: ", timestamp, (int)(tv.tv_usec / 1000), level, func); - va_start(ap, fmt); - n += vfprintf(fp, fmt, ap); - va_end(ap); - n += fprintf(fp, " (%s:%d)\n", file, line); - funlockfile(fp); - return n; +int lprintf(FILE *fp, int level, const char *file, int line, const char *func, + const char *fmt, ...) { + struct timeval tv; + struct tm tm; + char timestamp[32]; + int n = 0; + va_list ap; + + flockfile(fp); + gettimeofday(&tv, NULL); + strftime(timestamp, sizeof(timestamp), "%T", localtime_r(&tv.tv_sec, &tm)); + n += fprintf(fp, "%s.%03d [%c] %s: ", timestamp, (int)(tv.tv_usec / 1000), + level, func); + va_start(ap, fmt); + n += vfprintf(fp, fmt, ap); + va_end(ap); + n += fprintf(fp, " (%s:%d)\n", file, line); + funlockfile(fp); + return n; } -void -hexdump(FILE *fp, const void *data, size_t size) -{ - unsigned char *src; - int offset, index; - - flockfile(fp); - src = (unsigned char *)data; - fprintf(fp, "+------+-------------------------------------------------+------------------+\n"); - for(offset = 0; offset < (int)size; offset += 16) { - fprintf(fp, "| %04x | ", offset); - for(index = 0; index < 16; index++) { - if(offset + index < (int)size) { - fprintf(fp, "%02x ", 0xff & src[offset + index]); - } else { - fprintf(fp, " "); - } +void hexdump(FILE *fp, const void *data, size_t size) { + unsigned char *src; + int offset, index; + + flockfile(fp); + src = (unsigned char *)data; + fprintf(fp, "+------+-------------------------------------------------+------" + "------------+\n"); + for (offset = 0; offset < (int)size; offset += 16) { + fprintf(fp, "| %04x | ", offset); + for (index = 0; index < 16; index++) { + if (offset + index < (int)size) { + fprintf(fp, "%02x ", 0xff & src[offset + index]); + } else { + fprintf(fp, " "); + } + } + fprintf(fp, "| "); + for (index = 0; index < 16; index++) { + if (offset + index < (int)size) { + if (isascii(src[offset + index]) && isprint(src[offset + index])) { + fprintf(fp, "%c", src[offset + index]); + } else { + fprintf(fp, "."); } - fprintf(fp, "| "); - for(index = 0; index < 16; index++) { - if(offset + index < (int)size) { - if(isascii(src[offset + index]) && isprint(src[offset + index])) { - fprintf(fp, "%c", src[offset + index]); - } else { - fprintf(fp, "."); - } - } else { - fprintf(fp, " "); - } - } - fprintf(fp, " |\n"); - } - fprintf(fp, "+------+-------------------------------------------------+------------------+\n"); - funlockfile(fp); + } else { + fprintf(fp, " "); + } + } + fprintf(fp, " |\n"); + } + fprintf(fp, "+------+-------------------------------------------------+------" + "------------+\n"); + funlockfile(fp); } /* @@ -79,83 +79,74 @@ hexdump(FILE *fp, const void *data, size_t size) */ struct queue_entry { - struct queue_entry *next; - void *data; + struct queue_entry *next; + void *data; }; -void -queue_init(struct queue_head *queue) -{ - queue->head = NULL; - queue->tail = NULL; - queue->num = 0; +void queue_init(struct queue_head *queue) { + queue->head = NULL; + queue->tail = NULL; + queue->num = 0; } -void * -queue_push(struct queue_head *queue, void *data) -{ - struct queue_entry *entry; - - if (!queue) { - return NULL; - } - entry = memory_alloc(sizeof(*entry)); - if (!entry) { - return NULL; - } - entry->next = NULL; - entry->data = data; - if (queue->tail) { - queue->tail->next = entry; - } - queue->tail = entry; - if (!queue->head) { - queue->head = entry; - } - queue->num++; - return data; +void *queue_push(struct queue_head *queue, void *data) { + struct queue_entry *entry; + + if (!queue) { + return NULL; + } + entry = memory_alloc(sizeof(*entry)); + if (!entry) { + return NULL; + } + entry->next = NULL; + entry->data = data; + if (queue->tail) { + queue->tail->next = entry; + } + queue->tail = entry; + if (!queue->head) { + queue->head = entry; + } + queue->num++; + return data; } -void * -queue_pop(struct queue_head *queue) -{ - struct queue_entry *entry; - void *data; +void *queue_pop(struct queue_head *queue) { + struct queue_entry *entry; + void *data; - if (!queue || !queue->head) { - return NULL; - } - entry = queue->head; - queue->head = entry->next; - if (!queue->head) { - queue->tail = NULL; - } - queue->num--; - data = entry->data; - memory_free(entry); - return data; + if (!queue || !queue->head) { + return NULL; + } + entry = queue->head; + queue->head = entry->next; + if (!queue->head) { + queue->tail = NULL; + } + queue->num--; + data = entry->data; + memory_free(entry); + return data; } -void * -queue_peek(struct queue_head *queue) -{ - if (!queue || !queue->head) { - return NULL; - } - return queue->head->data; +void *queue_peek(struct queue_head *queue) { + if (!queue || !queue->head) { + return NULL; + } + return queue->head->data; } -void -queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), void *arg) -{ - struct queue_entry *entry; +void queue_foreach(struct queue_head *queue, + void (*func)(void *arg, void *data), void *arg) { + struct queue_entry *entry; - if (!queue || !func) { - return; - } - for (entry = queue->head; entry; entry = entry->next) { - func(arg, entry->data); - } + if (!queue || !func) { + return; + } + for (entry = queue->head; entry; entry = entry->next) { + func(arg, entry->data); + } } /* @@ -171,80 +162,66 @@ queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), voi static int endian; -static int -byteorder(void) { - uint32_t x = 0x00000001; +static int byteorder(void) { + uint32_t x = 0x00000001; - return *(uint8_t *)&x ? __LITTLE_ENDIAN : __BIG_ENDIAN; + return *(uint8_t *)&x ? __LITTLE_ENDIAN : __BIG_ENDIAN; } -static uint16_t -byteswap16(uint16_t v) -{ - return (v & 0x00ff) << 8 | (v & 0xff00 ) >> 8; +static uint16_t byteswap16(uint16_t v) { + return (v & 0x00ff) << 8 | (v & 0xff00) >> 8; } -static uint32_t -byteswap32(uint32_t v) -{ - return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24; +static uint32_t byteswap32(uint32_t v) { + return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | + (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24; } -uint16_t -hton16(uint16_t h) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap16(h) : h; +uint16_t hton16(uint16_t h) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap16(h) : h; } -uint16_t -ntoh16(uint16_t n) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap16(n) : n; +uint16_t ntoh16(uint16_t n) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap16(n) : n; } -uint32_t -hton32(uint32_t h) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap32(h) : h; +uint32_t hton32(uint32_t h) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap32(h) : h; } -uint32_t -ntoh32(uint32_t n) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap32(n) : n; +uint32_t ntoh32(uint32_t n) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap32(n) : n; } /* * Checksum */ -uint16_t -cksum16(uint16_t *addr, uint16_t count, uint32_t init) -{ - uint32_t sum; - - sum = init; - while (count > 1) { - sum += *(addr++); - count -= 2; - } - if (count > 0) { - sum += *(uint8_t *)addr; - } - while (sum >> 16) { - sum = (sum & 0xffff) + (sum >> 16); - } - return ~(uint16_t)sum; +uint16_t cksum16(uint16_t *addr, uint16_t count, uint32_t init) { + uint32_t sum; + + sum = init; + while (count > 1) { + sum += *(addr++); + count -= 2; + } + if (count > 0) { + sum += *(uint8_t *)addr; + } + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + return ~(uint16_t)sum; } diff --git a/util.h b/util.h index ea4e491d..c48e1a33 100644 --- a/util.h +++ b/util.h @@ -1,8 +1,8 @@ #ifndef UTIL_H #define UTIL_H -#include #include +#include #include /* @@ -28,34 +28,38 @@ * Time */ -#define timeval_add_usec(x, y) \ - do { \ - (x)->tv_sec += y / 1000000; \ - (x)->tv_usec += y % 1000000; \ - if ((x)->tv_usec >= 1000000) { \ - (x)->tv_sec += 1; \ - (x)->tv_usec -= 1000000; \ - } \ - } while(0); - -#define timespec_add_nsec(x, y) \ - do { \ - (x)->tv_sec += y / 1000000000; \ - (x)->tv_nsec += y % 1000000000; \ - if ((x)->tv_nsec >= 1000000000) { \ - (x)->tv_sec += 1; \ - (x)->tv_nsec -= 1000000000; \ - } \ - } while(0); +#define timeval_add_usec(x, y) \ + do { \ + (x)->tv_sec += y / 1000000; \ + (x)->tv_usec += y % 1000000; \ + if ((x)->tv_usec >= 1000000) { \ + (x)->tv_sec += 1; \ + (x)->tv_usec -= 1000000; \ + } \ + } while (0); + +#define timespec_add_nsec(x, y) \ + do { \ + (x)->tv_sec += y / 1000000000; \ + (x)->tv_nsec += y % 1000000000; \ + if ((x)->tv_nsec >= 1000000000) { \ + (x)->tv_sec += 1; \ + (x)->tv_nsec -= 1000000000; \ + } \ + } while (0); /* * Logging */ -#define errorf(...) lprintf(stderr, 'E', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define warnf(...) lprintf(stderr, 'W', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define infof(...) lprintf(stderr, 'I', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define debugf(...) lprintf(stderr, 'D', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define errorf(...) \ + lprintf(stderr, 'E', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define warnf(...) \ + lprintf(stderr, 'W', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define infof(...) \ + lprintf(stderr, 'I', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define debugf(...) \ + lprintf(stderr, 'D', __FILE__, __LINE__, __func__, __VA_ARGS__) #ifdef HEXDUMP #define debugdump(...) hexdump(stderr, __VA_ARGS__) @@ -63,10 +67,9 @@ #define debugdump(...) #endif -extern int -lprintf(FILE *fp, int level, const char *file, int line, const char *func, const char *fmt, ...); -extern void -hexdump(FILE *fp, const void *data, size_t size); +extern int lprintf(FILE *fp, int level, const char *file, int line, + const char *func, const char *fmt, ...); +extern void hexdump(FILE *fp, const void *data, size_t size); /* * Queue @@ -75,40 +78,31 @@ hexdump(FILE *fp, const void *data, size_t size); struct queue_entry; struct queue_head { - struct queue_entry *head; - struct queue_entry *tail; - unsigned int num; + struct queue_entry *head; + struct queue_entry *tail; + unsigned int num; }; -extern void -queue_init(struct queue_head *queue); -extern void * -queue_push(struct queue_head *queue, void *data); -extern void * -queue_pop(struct queue_head *queue); -extern void * -queue_peek(struct queue_head *queue); -extern void -queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), void *arg); +extern void queue_init(struct queue_head *queue); +extern void *queue_push(struct queue_head *queue, void *data); +extern void *queue_pop(struct queue_head *queue); +extern void *queue_peek(struct queue_head *queue); +extern void queue_foreach(struct queue_head *queue, + void (*func)(void *arg, void *data), void *arg); /* * Byteorder */ -extern uint16_t -hton16(uint16_t h); -extern uint16_t -ntoh16(uint16_t n); -extern uint32_t -hton32(uint32_t h); -extern uint32_t -ntoh32(uint32_t n); +extern uint16_t hton16(uint16_t h); +extern uint16_t ntoh16(uint16_t n); +extern uint32_t hton32(uint32_t h); +extern uint32_t ntoh32(uint32_t n); /* * Checksum */ -extern uint16_t -cksum16(uint16_t *addr, uint16_t count, uint32_t init); +extern uint16_t cksum16(uint16_t *addr, uint16_t count, uint32_t init); #endif From 3266de4f1f38e28e89939bbf11a88254a44769d1 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 15:29:18 +0000 Subject: [PATCH 07/36] chore: add format rule --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8e91d6c0..8d5a70fe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "editor.formatOnSave": true, + "editor.formatOnPaste": false, "clang-format.executable": "/usr/bin/clang-format", "[c]": { "editor.defaultFormatter": "xaver.clang-format", From 474f9d1b6ddf43f89eca4120ac21f8f47b2545bf Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 15:29:52 +0000 Subject: [PATCH 08/36] feat: add empty implementation for network device --- driver/dummy.c | 17 ++++++++++++++ driver/dummy.h | 8 +++++++ net.c | 33 ++++++++++++++++++++++++++ net.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/step1.c | 19 +++++++++++++++ 5 files changed, 140 insertions(+) diff --git a/driver/dummy.c b/driver/dummy.c index e69de29b..ac3a516e 100644 --- a/driver/dummy.c +++ b/driver/dummy.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#include "net.h" +#include "util.h" + +#define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram */ + +static int dummy_transmit(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst) {} + +static struct net_device_ops dummy_ops = { + .transmit = dummy_transmit, +}; + +struct net_device *dummy_init(void) {} diff --git a/driver/dummy.h b/driver/dummy.h index e69de29b..6a4fe9ec 100644 --- a/driver/dummy.h +++ b/driver/dummy.h @@ -0,0 +1,8 @@ +#ifndef DUMMY_H +#define DUMMY_H + +#include "net.h" + +extern struct net_device *dummy_init(void); + +#endif diff --git a/net.c b/net.c index e69de29b..34d5c995 100644 --- a/net.c +++ b/net.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "platform.h" + +#include "net.h" +#include "util.h" + +/* NOTE: if you want to add/delete the entries after net_run(), you need to + * protect these lists with a mutex. */ +static struct net_device *devices; + +struct net_device *net_device_alloc(void) {} + +/* NOTE: must not be call after net_run() */ +int net_device_register(struct net_device *dev) {} + +static int net_device_open(struct net_device *dev) {} + +static int net_device_close(struct net_device *dev) {} + +int net_device_output(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst) {} + +int net_input_handler(uint16_t type, const uint8_t *data, size_t len, + struct net_device *dev) {} + +int net_run(void) {} + +void net_shutdown(void) {} + +int net_init(void) {} diff --git a/net.h b/net.h index e69de29b..09b41d8c 100644 --- a/net.h +++ b/net.h @@ -0,0 +1,63 @@ +#ifndef NET_H +#define NET_H + +#include +#include + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +#define NET_DEVICE_TYPE_DUMMY 0x0000 +#define NET_DEVICE_TYPE_LOOPBACK 0x0001 +#define NET_DEVICE_TYPE_ETHERNET 0x0002 + +#define NET_DEVICE_FLAG_UP 0x0001 +#define NET_DEVICE_FLAG_LOOPBACK 0x0010 +#define NET_DEVICE_FLAG_BROADCAST 0x0020 +#define NET_DEVICE_FLAG_P2P 0x0040 +#define NET_DEVICE_FLAG_NEED_ARP 0x0100 + +#define NET_DEVICE_ADDR_LEN 16 + +#define NET_DEVICE_IS_UP(x) ((x)->flags & NET_DEVICE_FLAG_UP) +#define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down") + +struct net_device { + struct net_device *next; + unsigned int index; + char name[IFNAMSIZ]; + uint16_t type; + uint16_t mtu; + uint16_t flags; + uint16_t hlen; /* header length */ + uint16_t alen; /* address length */ + uint8_t addr[NET_DEVICE_ADDR_LEN]; + union { + uint8_t peer[NET_DEVICE_ADDR_LEN]; + uint8_t broadcast[NET_DEVICE_ADDR_LEN]; + }; + struct net_device_ops *ops; + void *priv; +}; + +struct net_device_ops { + int (*open)(struct net_device *dev); + int (*close)(struct net_device *dev); + int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst +; +}; + +extern struct net_device *net_device_alloc(void); +extern int net_device_register(struct net_device *dev); +extern int net_device_output(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst); + +extern int net_input_handler(uint16_t type, const uint8_t *data, size_t len, + struct net_device *dev); + +extern int net_run(void); +extern void net_shutdown(void); +extern int net_init(void); + +#endif diff --git a/test/step1.c b/test/step1.c index e69de29b..e17f6e5e 100644 --- a/test/step1.c +++ b/test/step1.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "net.h" +#include "util.h" + +#include "driver/dummy.h" + +#include "test.h" + +static volatile sig_atomic_t terminate; + +static void on_signal(int s) { + (void)s; + terminate = 1; +} + +int main(int argc, char *argv[]) {} From 622801b48829e3597016e4fbd6f0aefe92324934 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 16:21:49 +0000 Subject: [PATCH 09/36] fix: syntax error --- net.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net.h b/net.h index 09b41d8c..9fce4c3c 100644 --- a/net.h +++ b/net.h @@ -24,14 +24,18 @@ #define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down") struct net_device { - struct net_device *next; + struct net_device *next; // pointer to the next net_device unsigned int index; char name[IFNAMSIZ]; - uint16_t type; + uint16_t + type; // type of the net_device (defined in net.h as NET_DEVICE_TYPE_XXX) + // ------------------------------------------------ + // varies by the type of the net_device uint16_t mtu; uint16_t flags; uint16_t hlen; /* header length */ uint16_t alen; /* address length */ + // ------------------------------------------------ uint8_t addr[NET_DEVICE_ADDR_LEN]; union { uint8_t peer[NET_DEVICE_ADDR_LEN]; @@ -44,8 +48,8 @@ struct net_device { struct net_device_ops { int (*open)(struct net_device *dev); int (*close)(struct net_device *dev); - int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst -; + int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, + size_t len, const void *dst); }; extern struct net_device *net_device_alloc(void); From 3750cb1218986f448b664a81d3961291c1946002 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 16:22:50 +0000 Subject: [PATCH 10/36] chore: add LSP setting --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8d5a70fe..b08c5430 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "editor.formatOnSave": true, "editor.formatOnPaste": false, "clang-format.executable": "/usr/bin/clang-format", + "clangd.path": "/usr/bin/clangd", "[c]": { "editor.defaultFormatter": "xaver.clang-format", } From 02abb876b342cad31f9acd7c14ed24b88e5276e6 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 21 Sep 2025 17:02:48 +0000 Subject: [PATCH 11/36] chore: add editor settings --- .vscode/settings.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index b08c5430..25e2d155 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,7 @@ "clangd.path": "/usr/bin/clangd", "[c]": { "editor.defaultFormatter": "xaver.clang-format", + "editor.tabSize": 2, + "editor.indentSize": "tabSize", } } From 3ddb2be0cfb03a1828ac4a2a0d9d7af98708f038 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 00:28:18 +0900 Subject: [PATCH 12/36] chore: add comments about net_device --- net.h | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/net.h b/net.h index 9fce4c3c..2ee3f3d9 100644 --- a/net.h +++ b/net.h @@ -23,31 +23,49 @@ #define NET_DEVICE_IS_UP(x) ((x)->flags & NET_DEVICE_FLAG_UP) #define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down") +// ネットワークデバイスの定義 +// ref: +// https://docs.google.com/presentation/d/1ID6ggxASfc_1bWiJfDy1IFKIwzxvfYy8rUBrWYTRFj8/edit?slide=id.gd328c3072b_0_1161#slide=id.gd328c3072b_0_1161 struct net_device { - struct net_device *next; // pointer to the next net_device + // 次のデバイスへのポインタ + struct net_device *next; unsigned int index; char name[IFNAMSIZ]; - uint16_t - type; // type of the net_device (defined in net.h as NET_DEVICE_TYPE_XXX) - // ------------------------------------------------ - // varies by the type of the net_device - uint16_t mtu; + + // デバイスの種別(net.h に NET_DEVICE_TYPE_XXX として定義) + uint16_t type; + + // ----- デバイスの種別によって変化する値 --------------- + uint16_t mtu; // デバイスの MTU (Maximum Transmission Unit) の値 uint16_t flags; uint16_t hlen; /* header length */ uint16_t alen; /* address length */ // ------------------------------------------------ + + // デバイスのハードウェアアドレス + // - デバイスによってアドレスサイズが異なるので大きめのバッファを用意 + // - アドレスを持たないデバイスでは値は設定されない uint8_t addr[NET_DEVICE_ADDR_LEN]; union { uint8_t peer[NET_DEVICE_ADDR_LEN]; uint8_t broadcast[NET_DEVICE_ADDR_LEN]; }; + + // デバイスドライバに実装されている関数が + // 設定された struct net_device_ops へのポインタ struct net_device_ops *ops; + + // デバイスドライバが使用するプライベートなデータへのポインタ void *priv; }; +// デバイスドライバに実装されている関数へのポインタを格納 struct net_device_ops { + // optional int (*open)(struct net_device *dev); + // optional int (*close)(struct net_device *dev); + // required: 送信関数 transmit は必須 int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst); }; From 8440b3cb5d17b7f64ed1abcceb4d69e2870af881 Mon Sep 17 00:00:00 2001 From: nukopy Date: Thu, 25 Sep 2025 15:30:09 +0000 Subject: [PATCH 13/36] feat: generate and register network device --- net.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/net.c b/net.c index 34d5c995..d67ef04e 100644 --- a/net.c +++ b/net.c @@ -9,12 +9,54 @@ /* NOTE: if you want to add/delete the entries after net_run(), you need to * protect these lists with a mutex. */ +// デバイスリスト(リストの先頭を指すポインタ) static struct net_device *devices; -struct net_device *net_device_alloc(void) {} +struct net_device *net_device_alloc(void) { + // pointer to new net_device + struct net_device *dev; + + // デバイス構造体のサイズのメモリを確保 + // - memory_alloc() で確保したメモリ領域は 0 で初期化されている + // - メモリが確保できなかったらエラーとして NULL を返す + // - メモリの確保と解放には memory_alloc() と memory_free() を使う + dev = memory_alloc(sizeof(*dev)); + if (!dev) { + errorf("memory_alloc() failure"); + return NULL; + } + + return dev; +} /* NOTE: must not be call after net_run() */ -int net_device_register(struct net_device *dev) {} +// デバイスの登録 +int net_device_register(struct net_device *dev) { + // (関数の初回実行時だけ動く)デバイスのインデックス番号の初期化 + // note: 関数内で static な変数を一度だけ初期化し、関数呼出し間で保持できる。 + // note: 関数に副作用を入れることになる。 + // note: マルチスレッド環境でのデータ競合に注意。 + static unsigned int index = 0; + + // デバイスのインデックス番号の設定 + // note: 後置インクリメントなので現在の値を代入してからインクリメントされる + dev->index = index++; // set, then increment + + // デバイス名を生成する(net0, net1, net2, ...) + snprintf(dev->name, sizeof(dev->name), "net%d", dev->index); + + // デバイスリストの先頭にデバイスを追加 + // ref: Day 1 - デバイスの生成と登録 + // https://docs.google.com/presentation/d/1ID6ggxASfc_1bWiJfDy1IFKIwzxvfYy8rUBrWYTRFj8/edit?slide=id.gd328c3072b_0_1179#slide=id.gd328c3072b_0_1179 + // 新規登録するデバイスの次のデバイスを、現在のデバイスリストの先頭のデバイスに設定する + dev->next = devices; + // 現在のデバイスリストの先頭のデバイスを新規登録するデバイスに設定する + devices = dev; + + infof("registered, dev=%s, type=0x%04x", dev->name, dev->type); + + return 0; +} static int net_device_open(struct net_device *dev) {} From f2291df34c4ed77234e2f69578348f4c17335ca3 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 01:01:04 +0900 Subject: [PATCH 14/36] feat: enhance logging --- net.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net.c b/net.c index d67ef04e..1091f667 100644 --- a/net.c +++ b/net.c @@ -50,10 +50,14 @@ int net_device_register(struct net_device *dev) { // https://docs.google.com/presentation/d/1ID6ggxASfc_1bWiJfDy1IFKIwzxvfYy8rUBrWYTRFj8/edit?slide=id.gd328c3072b_0_1179#slide=id.gd328c3072b_0_1179 // 新規登録するデバイスの次のデバイスを、現在のデバイスリストの先頭のデバイスに設定する dev->next = devices; + // 現在のデバイスリストの先頭のデバイスを新規登録するデバイスに設定する devices = dev; + infof("registered device: dev=%s, type=0x%04x", dev->name, dev->type); + + return 0; +} - infof("registered, dev=%s, type=0x%04x", dev->name, dev->type); return 0; } From 5e5fd4162e754560f661d741c9c516de370836f8 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 01:02:46 +0900 Subject: [PATCH 15/36] chore: add comments for network devices and its flags --- net.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net.h b/net.h index 2ee3f3d9..a24f0faf 100644 --- a/net.h +++ b/net.h @@ -12,14 +12,21 @@ #define NET_DEVICE_TYPE_LOOPBACK 0x0001 #define NET_DEVICE_TYPE_ETHERNET 0x0002 +// NET_DEVICE_FLAG_UP: 0000_0000_0000_0001 = 1 #define NET_DEVICE_FLAG_UP 0x0001 +// NET_DEVICE_FLAG_LOOPBACK: 0000_0000_0001_0000 = 16 #define NET_DEVICE_FLAG_LOOPBACK 0x0010 +// NET_DEVICE_FLAG_BROADCAST: 0000_0000_0010_0000 = 32 #define NET_DEVICE_FLAG_BROADCAST 0x0020 +// NET_DEVICE_FLAG_P2P: 0000_0000_0100_0000 = 64 #define NET_DEVICE_FLAG_P2P 0x0040 +// NET_DEVICE_FLAG_NEED_ARP: 0000_0001_0000_0000 = 256 (= 16^2 * 1) #define NET_DEVICE_FLAG_NEED_ARP 0x0100 #define NET_DEVICE_ADDR_LEN 16 +// UP flag が立っていればビット演算の論理積 AND の値が非 0 になり、 +// truthy な値となる #define NET_DEVICE_IS_UP(x) ((x)->flags & NET_DEVICE_FLAG_UP) #define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down") @@ -51,8 +58,8 @@ struct net_device { uint8_t broadcast[NET_DEVICE_ADDR_LEN]; }; - // デバイスドライバに実装されている関数が - // 設定された struct net_device_ops へのポインタ + // デバイスドライバに実装されている関数を + // 格納している構造体 struct net_device_ops へのポインタ struct net_device_ops *ops; // デバイスドライバが使用するプライベートなデータへのポインタ From 6631a0cf1944fccd60238e1ba35f71c6910fee59 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 01:03:11 +0900 Subject: [PATCH 16/36] feat: open / close network device --- net.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/net.c b/net.c index 1091f667..3ab2b1a1 100644 --- a/net.c +++ b/net.c @@ -58,13 +58,55 @@ int net_device_register(struct net_device *dev) { return 0; } +static int net_device_open(struct net_device *dev) { + // デバイスの状態を確認(既に UP 状態の場合はエラーを返す) + if (NET_DEVICE_IS_UP(dev)) { + errorf("device is already opened: dev=%s", dev->name); + return -1; + } + + // デバイスドライバのオープン関数を呼び出す + // - オープン関数が設定されていない場合は呼び出しをスキップ + // - エラーが返されたらこの関数もエラーを返す + if (dev->ops->open) { + if (dev->ops->open(dev) == -1) { + errorf("failed to open device: dev=%s", dev->name); + return -1; + } + } + + // デバイスのオープンに成功したら UP フラグを立てる + dev->flags |= NET_DEVICE_FLAG_UP; + infof("opened device status: dev=%s, state=%s", dev->name, + NET_DEVICE_STATE(dev)); return 0; } -static int net_device_open(struct net_device *dev) {} +static int net_device_close(struct net_device *dev) { + // デバイスの状態を確認 + // UP 状態でないデバイスを close しようとしたときはエラーを返す + if (!NET_DEVICE_IS_UP(dev)) { + errorf("device is not opened: dev=%s", dev->name); + } + + // デバイスドライバのクローズ関数を呼び出す + // - クローズ関数が設定されていない場合は呼び出しをスキップ + // - エラーが返されたらこの関数もエラーを返す + if (dev->ops->close) { + if (dev->ops->close(dev) == -1) { + errorf("failed to close device: dev=%s"); + return -1; + } + } + + // UP フラグを落とす + dev->flags &= ~NET_DEVICE_FLAG_UP; + infof("closed device status: dev=%s, state=%s", dev->name, + NET_DEVICE_STATE(dev)); -static int net_device_close(struct net_device *dev) {} + return 0; +} int net_device_output(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst) {} From 2dabe1dc745760f3786985d0692cde4b60d9c0e6 Mon Sep 17 00:00:00 2001 From: nukopy Date: Thu, 25 Sep 2025 17:41:26 +0000 Subject: [PATCH 17/36] feat: output data to device --- net.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/net.c b/net.c index 3ab2b1a1..5d5521d3 100644 --- a/net.c +++ b/net.c @@ -108,8 +108,42 @@ static int net_device_close(struct net_device *dev) { return 0; } +// デバイスへの出力 int net_device_output(struct net_device *dev, uint16_t type, - const uint8_t *data, size_t len, const void *dst) {} + const uint8_t *data, size_t len, const void *dst) { + // デバイスの状態を確認 + // UP 状態でないデバイスに出力する場合はエラーを返す + if (!NET_DEVICE_IS_UP(dev)) { + errorf("device is not opened on transmit: dev=%s", dev->name); + return -1; + } + + // データのサイズを確認 + // デバイスの MTU を超えるサイズのデータは送信できないのでエラーを返す + if (len > dev->mtu) { + errorf("data is too long on transmit: dev=%s, mtu=%u, len=%zu", dev->name, + dev->mtu, len); + return -1; + } + /* note: フォーマット指定子 %04x について + + - `%04x`: 値を 16 進数で出力する。ゼロ 4 桁で padding。 + - 幅を 4 桁に指定し、ゼロで埋める (`%0` + `4` + `x`) + - 16 進数で足りない桁は先頭を 0 で埋めて 4 桁で出力する + */ + debugf("transmit to device: dev=%s, type=0x%04x, len=%zu", dev->name, type, + len); + debugdump(data, len); + + // デバイスドライバの出力関数を呼び出す + // エラーが返されたらこの関数もエラーを返す + if (dev->ops->transmit(dev, type, data, len, dst) == -1) { + errorf("device transmit failure: dev=%s, len=%zu", dev->name, len); + return -1; + } + + return 0; +} int net_input_handler(uint16_t type, const uint8_t *data, size_t len, struct net_device *dev) {} From e7ec4f6d8d15fe52c7b5fc1d53b902c1a0b74db5 Mon Sep 17 00:00:00 2001 From: nukopy Date: Thu, 25 Sep 2025 17:45:30 +0000 Subject: [PATCH 18/36] [wip] feat: input handler for data from network device --- net.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/net.c b/net.c index 5d5521d3..b4e0bca2 100644 --- a/net.c +++ b/net.c @@ -131,22 +131,49 @@ int net_device_output(struct net_device *dev, uint16_t type, - 幅を 4 桁に指定し、ゼロで埋める (`%0` + `4` + `x`) - 16 進数で足りない桁は先頭を 0 で埋めて 4 桁で出力する */ - debugf("transmit to device: dev=%s, type=0x%04x, len=%zu", dev->name, type, + debugf("output to device: dev=%s, type=0x%04x, len=%zu", dev->name, type, len); debugdump(data, len); // デバイスドライバの出力関数を呼び出す // エラーが返されたらこの関数もエラーを返す if (dev->ops->transmit(dev, type, data, len, dst) == -1) { - errorf("device transmit failure: dev=%s, len=%zu", dev->name, len); + errorf("device transmit failure on output: dev=%s, len=%zu", dev->name, + len); return -1; } return 0; } +/* デバイスからの入力: デバイスが受信したパケットをプロトコルスタックに渡す関数 + +・プロトコルスタックへのデータの入口であり、デバイスドライバから呼び出されることを想定している +・複数のデバイスドライバからの入力を一手に受け取る + + デバイスドライからこの関数が呼び出されるイメージ: + + +----------------------+ + | net_input_handler() | + +-----------^----------+ + | + +------------+------------+ + | | + +--------------+ +--------------+ + | Device Driver| | Device Driver| + +------^-------+ +------^-------+ + | | + Device Device +*/ int net_input_handler(uint16_t type, const uint8_t *data, size_t len, - struct net_device *dev) {} + struct net_device *dev) { + // TODO: 今の段階では呼び出されたことがわかればよいのでデバッグ出力のみ + debugf("received data from device: dev=%s, type=0x%04x, len=%zu", dev->name, + type, len); + debugdump(data, len); + + return 0; +} int net_run(void) {} From 2a6999e607eef2e4c75ee2a9fe5323410f902cbd Mon Sep 17 00:00:00 2001 From: nukopy Date: Thu, 25 Sep 2025 17:57:47 +0000 Subject: [PATCH 19/36] fix: not specifed variable to format specifier --- net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net.c b/net.c index b4e0bca2..063dcad6 100644 --- a/net.c +++ b/net.c @@ -95,7 +95,7 @@ static int net_device_close(struct net_device *dev) { // - エラーが返されたらこの関数もエラーを返す if (dev->ops->close) { if (dev->ops->close(dev) == -1) { - errorf("failed to close device: dev=%s"); + errorf("failed to close device: dev=%s", dev->name); return -1; } } From 4a8448c746c22d0e373a38a34234b64178051b72 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 04:17:26 +0900 Subject: [PATCH 20/36] feat: impl run / shutdown of protocol stack --- net.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/net.c b/net.c index 063dcad6..7e91b4c2 100644 --- a/net.c +++ b/net.c @@ -175,8 +175,65 @@ int net_input_handler(uint16_t type, const uint8_t *data, size_t len, return 0; } -int net_run(void) {} +/* プロトコルスタックの起動 -void net_shutdown(void) {} +## 呼び出しのコード例: net_run / net_shutdown -int net_init(void) {} +```c +int main(void) { + if (net_init() == -1) { + return -1; + } + + // デバイスの登録処理 + // ... + + // プロトコルスタックの起動 + if (net_run() == -1) { + return -1; + } + + // アプリケーションの処理 + // ... + + net_shutdown(); + + return 0; +} +``` +*/ +int net_run(void) { + struct net_device *dev; + + debugf("open all devices..."); + for (dev = devices; dev; dev = dev->next) { + if (net_device_open(dev) == -1) { + errorf("failed to open device on net_run: dev=%s"); + // 失敗しても for 文を抜けはしない + } + } + debugf("running..."); + + return 0; +} + +// プロトコルスタックの停止 +void net_shutdown(void) { + struct net_device *dev; + + debugf("close all devices..."); + for (dev = devices; dev; dev = dev->next) { + if (net_device_close(dev) == -1) { + errorf("failed to close device on shutting down: dev=%s"); + // 失敗しても for 文を抜けはしない + } + } + debugf("shutting down"); +} + +int net_init(void) { + // TODO: 今は何もしない。後に処理を追記。 + infof("initialized"); + + return 0; +} From 3d6fb0a439e7c11fe70c4a40bab143a64b802896 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 04:17:43 +0900 Subject: [PATCH 21/36] feat: impl dummy network device --- driver/dummy.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/driver/dummy.c b/driver/dummy.c index ac3a516e..21eff155 100644 --- a/driver/dummy.c +++ b/driver/dummy.c @@ -5,13 +5,60 @@ #include "net.h" #include "util.h" -#define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram */ +#define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram (0 ~ 65535) */ static int dummy_transmit(struct net_device *dev, uint16_t type, - const uint8_t *data, size_t len, const void *dst) {} + const uint8_t *data, size_t len, const void *dst) { + debugf("dummy transmit to device: dev=%s, type=0x%04x, len=%zu", dev->name, + type, len); + debugdump(data, len); + // データを破棄 + // ダミーデバイスなのでデータに対しては何もしない + return 0; +} + +// デバイスドライバに実装されている関数へのポインタを格納する構造体 +// ダミーデバイス用に transmit のみ登録 static struct net_device_ops dummy_ops = { .transmit = dummy_transmit, }; -struct net_device *dummy_init(void) {} +/* ダミーデバイスの初期化 + +## ダミーデバイスの仕様 + +- 入力:なし(データを受信することはない) +- 出力:データを破棄 +*/ +struct net_device *dummy_init(void) { + struct net_device *dev; + + // ダミーデバイスを生成 + dev = net_device_alloc(); + if (!dev) { + errorf("net_device_alloc() failure on initializing dummy device"); + return NULL; + } + + // ダミーデバイスの設定 + // note: dev->name は net_device_register 内で更新されるのでここでは設定しない + dev->type = NET_DEVICE_TYPE_DUMMY; + dev->mtu = DUMMY_MTU; + // ヘッダもアドレスも存在しない + dev->hlen = 0; + dev->alen = 0; + // デバイスドライバに実装されている関数へのポインタを格納する構造体 + dev->ops = &dummy_ops; + + // デバイスを登録 + if (net_device_register(dev) == -1) { + debugf("net_device_register() failure on initializaing dummy device: " + "dev=%s, type=0x%04x", + dev->name, dev->type); + return NULL; + } + debugf("dummy device initialized: dev=%s", dev->name); + + return dev; +} From 95d2563d33b7c2824dcc1af099542e4bd26c780e Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 04:19:42 +0900 Subject: [PATCH 22/36] feat: enhance logging on net_init --- net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net.c b/net.c index 7e91b4c2..515388e5 100644 --- a/net.c +++ b/net.c @@ -233,7 +233,7 @@ void net_shutdown(void) { int net_init(void) { // TODO: 今は何もしない。後に処理を追記。 - infof("initialized"); + infof("initialized protocol stack"); return 0; } From f8324f540f90efed866b778e8cb2d2c7ef6f7cf3 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 05:09:17 +0900 Subject: [PATCH 23/36] chore: add build target for step 1 --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 714b4578..cfb63439 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ APPS = -DRIVERS = +DRIVERS = driver/dummy.o OBJS = util.o \ + net.o \ TESTS = test/step0.exe \ + test/step1.exe \ CFLAGS := $(CFLAGS) -g -W -Wall -Wno-unused-parameter -iquote . From 5e9fa46812cfb45a46d30b202cef64ca81e0bcd3 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 05:10:48 +0900 Subject: [PATCH 24/36] feat: impl test of step 1 device management --- test/step1.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/test/step1.c b/test/step1.c index e17f6e5e..d30bb589 100644 --- a/test/step1.c +++ b/test/step1.c @@ -11,9 +11,81 @@ static volatile sig_atomic_t terminate; -static void on_signal(int s) { - (void)s; +/* シグナルハンドラ + +原則、シグナルハンドラの中では下記以外のことはしない: + +- 非同期安全な関数の呼び出し + - ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html +- volatile sig_atomic_t 型の変数への書き込み +*/ +static void on_signal(int signal) { + /* + 一般に、I/O 関数をシグナルハンドラ内で呼び出すのは安全ではない。 + シグナルハンドラ内で I/O 関数を使用する場合は、 + 使用するシステムの非同期安全な関数を事前に確認すること。 + */ + // warnf("on_signal: signal received %d", signal); + + /* `(void)signal;` は必要? + `(void)signal;` は「この引数は使っていない」ことを意図的に示す + ためのお決まりの書き方。多くのコンパイラは未使用引数に警告を出すので、 + それを抑止する目的で入れている。 + + - 既にハンドラ内で `signal` を使って何かするなら不要。 + - 使わないならそのまま残しておけば警告を避けられる。 + - 別の書き方として、 + - `__attribute__((unused))` + - `void on_signal(int \/\*signal\*\/)` + - のようにコメントで示す例もある。 + - 単に `(void)signal;` がもっとも広く使われる方法です。 + */ + + // (void)signal; terminate = 1; } -int main(int argc, char *argv[]) {} +int main(int argc, char *argv[]) { + struct net_device *dev; + + // シグナルハンドラの設定 + // Ctrl + C が押された際に行儀よく終了するようにする + signal(SIGINT, on_signal); + + // プロトコルスタックの初期化 + if (net_init() == -1) { + errorf("net_init() failure"); + return -1; + } + + // ダミーデバイスの初期化(ダミーデバイスの生成、設定、登録) + dev = dummy_init(); + + // プロトコルスタックの起動 + if (net_run() == -1) { + errorf("net_run() failure"); + return -1; + } + + // 1 秒おきにデバイスにパケットを書き込む + // まだパケットを自力で生成できないのでテストデータを用いる + uint16_t type = 0x0800; + size_t len = sizeof(test_data); + // Ctrl + C が押されるとシグナルハンドラ on_signal() の中で + // terminate に 1 が設定され、while ループを抜ける + while (!terminate) { + // Internet Layer -> Network Interface Layer への書き込み + // Internet Layer から来たパケットをデバイスに書き込む + if (net_device_output(dev, type, test_data, len, NULL) == -1) { + errorf("net_device_output() failure"); + break; + } + + sleep(1); + } + + // プロトコルスタックの停止 + net_shutdown(); + + return 0; +} From 243b7f3ddf49843175661b6df4b00f8a29e8bfc6 Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 05:11:12 +0900 Subject: [PATCH 25/36] DONE Day 1 - STEP 1 From 7be488290048155c07d4afd83e17b3f7ac982d0e Mon Sep 17 00:00:00 2001 From: nukopy Date: Fri, 26 Sep 2025 05:23:15 +0900 Subject: [PATCH 26/36] START Day 1 - STEP 2 From 8acd1090c05dcf4d2b9a6e9dcce6f8a36c994d2a Mon Sep 17 00:00:00 2001 From: nukopy Date: Thu, 25 Sep 2025 20:26:20 +0000 Subject: [PATCH 27/36] feat: add template for interruption (from commit 09d6772) --- driver/dummy.c | 6 ++++++ platform/linux/intr.c | 40 +++++++++++++++++++++++++++++++++++++++ platform/linux/platform.h | 18 ++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 platform/linux/intr.c diff --git a/driver/dummy.c b/driver/dummy.c index 21eff155..d5503593 100644 --- a/driver/dummy.c +++ b/driver/dummy.c @@ -2,11 +2,15 @@ #include #include +#include "platform.h" + #include "net.h" #include "util.h" #define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram (0 ~ 65535) */ +#define DUMMY_IRQ INTR_IRQ_BASE + static int dummy_transmit(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst) { debugf("dummy transmit to device: dev=%s, type=0x%04x, len=%zu", dev->name, @@ -24,6 +28,8 @@ static struct net_device_ops dummy_ops = { .transmit = dummy_transmit, }; +static int dummy_isr(unsigned int irq, void *id) {} + /* ダミーデバイスの初期化 ## ダミーデバイスの仕様 diff --git a/platform/linux/intr.c b/platform/linux/intr.c new file mode 100644 index 00000000..8ef9c30c --- /dev/null +++ b/platform/linux/intr.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +#include "platform.h" + +#include "util.h" + +struct irq_entry { + struct irq_entry *next; + unsigned int irq; + int (*handler)(unsigned int irq, void *dev); + int flags; + char name[16]; + void *dev; +}; + +/* NOTE: if you want to add/delete the entries after intr_run(), you need to + * protect these lists with a mutex. */ +static struct irq_entry *irqs; + +static sigset_t sigmask; + +static pthread_t tid; +static pthread_barrier_t barrier; + +int intr_request_irq(unsigned int irq, + int (*handler)(unsigned int irq, void *dev), int flags, + const char *name, void *dev) {} + +int intr_raise_irq(unsigned int irq) {} + +static void *intr_thread(void *arg) {} + +int intr_run(void) {} + +void intr_shutdown(void) {} + +int intr_init(void) {} diff --git a/platform/linux/platform.h b/platform/linux/platform.h index e1dd9107..d1d2cfa7 100644 --- a/platform/linux/platform.h +++ b/platform/linux/platform.h @@ -2,6 +2,7 @@ #define PLATFORM_H #include +#include #include #include @@ -33,4 +34,21 @@ static inline int mutex_unlock(mutex_t *mutex) { return pthread_mutex_unlock(mutex); } +/* + * Interrupt + */ + +#define INTR_IRQ_BASE (SIGRTMIN + 1) + +#define INTR_IRQ_SHARED 0x0001 + +extern int intr_request_irq(unsigned int irq, + int (*handler)(unsigned int irq, void *id), + int flags, const char *name, void *dev); +extern int intr_raise_irq(unsigned int irq); + +extern int intr_run(void); +extern void intr_shutdown(void); +extern int intr_init(void); + #endif From f1814b015422c1b68adce0ace76971e9be19030b Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:27:29 +0900 Subject: [PATCH 28/36] chore: add comments about using signal number as IRQ number on microps --- platform/linux/platform.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/platform/linux/platform.h b/platform/linux/platform.h index d1d2cfa7..15317618 100644 --- a/platform/linux/platform.h +++ b/platform/linux/platform.h @@ -38,6 +38,21 @@ static inline int mutex_unlock(mutex_t *mutex) { * Interrupt */ +// IRQ 番号の開始番号 +// +/* + +microps では IRQ 番号としてシグナル番号を使用する。 + +Linux では、SIGRTMIN ~ SIGRTMAX (34 ~ 64)までのシグナルを +アプリケーションが任意の目的で利用できる。 + +ただし、SIGRTMIN (34) に関しては、glibc が内部的に利用しているため、 ++1 した番号から利用するようにしている。 + +そのため、microps の IRQ 番号は 35 始まり。 + +*/ #define INTR_IRQ_BASE (SIGRTMIN + 1) #define INTR_IRQ_SHARED 0x0001 From 4cc025f7b49b2982ba0214e004ecd2824feedfc2 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:27:53 +0900 Subject: [PATCH 29/36] refactor: use readable function name --- platform/linux/platform.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/linux/platform.h b/platform/linux/platform.h index 15317618..3b70715d 100644 --- a/platform/linux/platform.h +++ b/platform/linux/platform.h @@ -57,9 +57,9 @@ Linux では、SIGRTMIN ~ SIGRTMAX (34 ~ 64)までのシグナルを #define INTR_IRQ_SHARED 0x0001 -extern int intr_request_irq(unsigned int irq, - int (*handler)(unsigned int irq, void *id), - int flags, const char *name, void *dev); +extern int intr_register_irq_entry(unsigned int irq, + int (*handler)(unsigned int irq, void *id), + int flags, const char *name, void *dev); extern int intr_raise_irq(unsigned int irq); extern int intr_run(void); From 50b10c94d07862eb396025a55568ddd81785e468 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:28:18 +0900 Subject: [PATCH 30/36] feat: impl interrupt processing --- platform/linux/intr.c | 304 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 296 insertions(+), 8 deletions(-) diff --git a/platform/linux/intr.c b/platform/linux/intr.c index 8ef9c30c..78b8df18 100644 --- a/platform/linux/intr.c +++ b/platform/linux/intr.c @@ -7,34 +7,322 @@ #include "util.h" +// 割り込み要求(IRQ, Interrupt Request)の構造体 +// ネットワークデバイス(net_device 構造体)と同様に linked list 構造で管理する struct irq_entry { + // 次の IRQ 構造体 struct irq_entry *next; + // 割り込み番号(IRQ 番号) unsigned int irq; + // 割り込みハンドラ(割り込みが発生した際に呼び出す関数へのポインタ) int (*handler)(unsigned int irq, void *dev); + // フラグ(INTR_IRQ_SHARED が指定された場合は IRQ 番号を共有可能) int flags; + // デバッグ出力で識別するための名前 char name[16]; + /* 割り込みの発生元となるデバイス + + ---------- + + struct net_device 以外にも対応できるように void * で保持 + */ void *dev; }; /* NOTE: if you want to add/delete the entries after intr_run(), you need to * protect these lists with a mutex. */ +// IRQ リスト(リストの先頭を指すポインタ) static struct irq_entry *irqs; +// シグナルマスク用のシグナル集合 static sigset_t sigmask; +// 割り込みスレッドのスレッド ID static pthread_t tid; +// スレッド間の同期のためのバリア static pthread_barrier_t barrier; +// バリアで同期するスレッドの数 +// (今回はメインスレッド、割り込み処理スレッドの 2 つ) +static const int NUM_THREADS_FOR_BARRIER = 2; + +/* 割り込み要求エントリ(IRQ エントリ)を IRQ リストに登録する + * + * ---------- + * + * ## microps における「割り込み」の実装 + * + * microps では、IRQ 番号にシグナル番号を使用する。 + * IRQ 番号は、ハードウェア割り込みにおいては、 + * 「どのハードウェアによって割り込み要求(IRQ)が発生したか」を識別するための数値。 + * microsはユーザ空間で動くので、カーネル空間で動くハードウェア割り込みの仕組みは使えない。 + * そこで処理の割り込みを行うためにシグナルを使ったソフトウェア割り込みを実装する。 + * ハードウェア割り込みを模倣した割り込みの仕組みをユーザ空間で実装する。 + * + * ## シグナルによりソフトウェア割り込みにマルチスレッドを使用する理由 + * + * シグナル受信時に非同期に実行されるシグナルハンドラでは実行できる処理が大きく制限される。 + * シグナル安全(signal-safe)、非同期安全な処理の範囲で処理を実装しなければならない。 + * + * ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html + * + * そのため、割り込み処理のために専用のスレッドを起動してシグナルの発生を待ち受けて処理する。 + * 割り込みの仕組みをマルチスレッドで実装する理由はこれ。 + * + */ +int intr_register_irq_entry(unsigned int irq, + int (*handler)(unsigned int irq, void *dev), + int flags, const char *name, void *dev) { + struct irq_entry *entry; + + debugf("registering new IRQ: irq=%u, flags=%d, name=%s", irq, flags, name); + + // -------------------------------------------------- + // IRQ 番号が既に登録されている場合、 + // IRQ 番号の共有が許可されているかどうかチェック + // どちらかが共有を許可していない場合はエラーを返す + // -------------------------------------------------- + + // IRQ リストを走査 + for (entry = irqs; entry; entry = entry->next) { + // IRQ リストに新規 IRQ エントリの IRQ 番号が既に登録されている場合、 + // その IRQ 番号に対して追加のハンドラを登録できるかどうか確認する。 + if (entry->irq == irq) { + /* + XOR(^) はビット単位の排他的論理和(2 つの値が異なるとき 1 になる) + 真理値表 (A ^ B): + A B | A^B + 0 0 | 0 + 0 1 | 1 + 1 0 | 1 + 1 1 | 0 + */ + /* + ・既存の IRQ のフラグが `INTR_IRQ_SHARED` と完全に一致しているか + - flags と INTR_IRQ_SHARED が一致している場合は XOR の結果は偽になる + - e.g. flags ^ INTR_IRQ_SHARED = 0001 ^ 0001 = 0000 -> 偽 + - flags と INTR_IRQ_SHARED が一致していない場合は XOR の結果は真 + - e.g. flags ^ INTR_IRQ_SHARED = 0000 ^ 0001 = 0001 -> 真 + ・新しく登録しようとしているフラグが`INTR_IRQ_SHARED`と完全に一致しているか + - + + をチェックしていて、どちらか一方でも値が違えば衝突とみなす。 + + 注意点として、XOR は「`INTR_IRQ_SHARED` + ビットが立っているかどうか」ではなく + 「値がまるごと同一かどうか」を判定している。 + フラグにほかのビットが含まれる場合は真になってしまうので、共有可否をビット単位で見る意図なら + `flags & INTR_IRQ_SHARED` のようにマスクした方が適切かもしれない。 + */ + + const int is_existing_irq_allows_share = + (entry->flags ^ INTR_IRQ_SHARED) == 0; + const int is_new_irq_allows_share = (flags ^ INTR_IRQ_SHARED) == 0; + const int is_irq_allows_share = + is_existing_irq_allows_share || is_new_irq_allows_share; + if (!is_irq_allows_share) { + errorf("conflicts with already registered IRQs"); + return -1; + } + } + } + + // -------------------------------------------------- + // IRQ リストへ新しいエントリを追加 + // -------------------------------------------------- + + // 新しい IRQ エントリのメモリを確保 + entry = memory_alloc(sizeof(*entry)); + if (!entry) { + errorf("memory_alloc() on registering IRQ entry failure"); + return -1; + } + + // IRQ エントリ構造体に値を設定 + entry->irq = irq; + entry->handler = handler; + entry->flags = flags; + strncpy(entry->name, name, sizeof(entry->name) - 1); + entry->dev = dev; + + // 新しい IRQ エントリを IRQ リストの先頭へ挿入 + entry->next = irqs; + irqs = entry; + + // シグナル集合へ新しいシグナルを追加 + // ここって既存のシグナルの被らないの? + // -> 被らない。既存のシグナルとの OR 演算なので。 + sigaddset(&sigmask, irq); + debugf("registered new IRQ entry: irq=%u, name=%s", entry->irq, entry->name); + + return 0; +} + +int intr_raise_irq(unsigned int irq) { + // 割り込み処理用のスレッドへシグナルを送信 + // microps では、IRQ 番号にシグナル番号を使用する + // 後続の処理では、 + // 発生したシグナルのシグナル番号と IRQ エントリの IRQ 番号が一致したときに + // IRQ エントリに登録されているハンドラが実行される + return pthread_kill(tid, (int)irq); +} + +// 割り込み処理を実行するスレッドのエントリーポイント +static void *intr_thread(void *arg) { + int terminate = 0; + int sig; + int err; + + struct irq_entry *entry; + + infof("start thread for interrupt"); + + // メインスレッドと同期を取るための処理 + pthread_barrier_wait(&barrier); + debugf("barrier unlocked on intr_thread!"); + + // ハードウェア割り込みに見立てたシグナルを処理するループ + while (!terminate) { + // ハードウェア割り込みに見立てたシグナルが発生するまで待機 + err = sigwait(&sigmask, &sig); + if (err) { + errorf("sigwait() failure: %s", strerror(err)); + break; + } + + // 発生したシグナルの種類に応じて処理 + switch (sig) { + // SIGHUP シグナルの処理: + // 割り込み処理用のスレッドへ終了を通知するためのシグナル + case SIGHUP: + terminate = 1; + break; + // デバイス割り込み用のシグナルを処理 + default: + // IRQ リストを巡回 + for (entry = irqs; entry; entry = entry->next) { + // IRQ 番号が一致するエントリの割り込みハンドラを呼び出す + // microps では、IRQ 番号にシグナル番号を使用する + if (entry->irq == (unsigned int)sig) { + debugf("call ISR: irq=%d, name=%s", entry->irq, entry->name); + entry->handler(entry->irq, entry->dev); + } + } + break; + } + } + warnf("terminated thread for interrupt"); + + return NULL; +} + +// 割り込み機構を起動する +// intr_init が呼ばれている状態でこの関数を呼ぶこと +int intr_run(void) { + int err; + + // シグナルマスクの設定 + // intr_init 内で sigmask の設定ができていることが前提 + err = pthread_sigmask(SIG_BLOCK, &sigmask, NULL); + if (err) { + errorf("pthread_sigmask"); + return -1; + } + + // 割り込み処理スレッドの起動 + err = pthread_create(&tid, NULL, intr_thread, NULL); + if (err) { + errorf("pthread_create() failed on intr_run: %s", strerror(err)); + return -1; + } + + // スレッドが動き出すまで待つ + // 他のスレッドが同じように pthread_barrier_wait() を呼び出し、 + // バリアのカウントが指定の数になるまでスレッドを停止する + infof("waiting for starting thread for interrupt with barrier..."); + pthread_barrier_wait(&barrier); + infof("thread for interrupt started!"); + + return 0; +} + +void intr_shutdown(void) { + int err; + + // 割り込み処理スレッドが起動済みかどうか確認 + /* Q. なぜ以下のコードで割り込み処理スレッドが起動済みかどうか確認できるのか? + * 前提:関数の実行順序は intr_init -> intr_run -> intr_shutdown + * + * `pthread_equal(tid, pthread_self())`は + *「今の呼び出し元スレッド(メインスレッド)の + * ID」と、グローバルに保持している `tid` を比較している。 + * + * ここの比較は `intr_init` の段階で `tid` に「メインスレッド(=呼び出し元) + * の `pthread_t`」を代入しておく前提になっている。 + * `intr_run` で割り込み処理スレッドを起動するときに + * `pthread_create(&tid, …)` として `tid` を新しいスレッドの ID で上書きする。 + * + * つまり、`intr_shutdown` をメインスレッドから呼ぶときに、 + * `tid` がまだメインスレッドと同じ ID のままなら + * 「新しいスレッドに置き換わっていない → スレッドを作れていない」= + *`pthread_equal` が真になる、という判定になる。 + * + * 逆に、スレッドが正常に起動済みなら `tid` は別の ID に差し替わっているので、 + * `pthread_equal` は偽になり、 `pthread_kill` や `pthread_join` + * に進む、という流れとなっている。 + + * 注意点として、このロジックは + * + * - `intr_init` をメインスレッドで呼ぶこと + * - 初期値として `tid = pthread_self()` をセットしていること + * - `intr_shutdown` をメインスレッドで呼ぶこと + * + * の 3 つが前提なので、別スレッドから呼び出したり、 + * 初期化を忘れたりすると意図通りにならない点には気をつける必要がある。 + */ + if (pthread_equal(tid, pthread_self()) != 0) { + warnf("thread not created"); + return; + } + + // 割り込み処理スレッドにシグナル(SIGHUP)を送信 + err = pthread_kill(tid, SIGHUP); + if (err) { + warnf("failed to kill process with SIGHUP: tid=%d, err=%s", tid, + strerror(err)); + return; + } + + // 割り込み処理スレッドが完全に終了するのを待つ + err = pthread_join(tid, NULL); + if (err) { + warnf("failed to pthread_join: tid=%d, err=%s", tid, strerror(err)); + return; + } + + infof("shutdown interrupt successfully!"); + + return; +} -int intr_request_irq(unsigned int irq, - int (*handler)(unsigned int irq, void *dev), int flags, - const char *name, void *dev) {} +int intr_init(void) { + infof("init interrupt"); -int intr_raise_irq(unsigned int irq) {} + // スレッド ID の初期値にメインスレッドの ID を設定する + tid = pthread_self(); -static void *intr_thread(void *arg) {} + // pthread_barrier の初期化(カウントを 2 に設定) + infof("init pthread barrier for %d threads", NUM_THREADS_FOR_BARRIER); + pthread_barrier_init(&barrier, NULL, NUM_THREADS_FOR_BARRIER); + debugf("barrier unlocked on intr_init!"); -int intr_run(void) {} + // グローバルな割り込み処理用のシグナル集合(シグナルマスク)を初期化(空にする) + infof("init sigmask"); + sigemptyset(&sigmask); -void intr_shutdown(void) {} + // シグナル集合に SIGHUP を追加(割り込みスレッド終了通知用) + sigaddset(&sigmask, SIGHUP); + // 注意:この段階ではまだプロセス(メインスレッド)にシグナルマスクは設定されていない + // pthread_sigmask が実行されて初めてシグナルマスクがプロセスに適用される -int intr_init(void) {} + return 0; +} From 3a0034666108850fae50bda13b859901f2c38d5b Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:28:36 +0900 Subject: [PATCH 31/36] feat: impl interrupt processing on dummy device --- driver/dummy.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/driver/dummy.c b/driver/dummy.c index d5503593..20201dec 100644 --- a/driver/dummy.c +++ b/driver/dummy.c @@ -9,6 +9,11 @@ #define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram (0 ~ 65535) */ +// ダミーデバイスが使う IRQ 番号 +// microps の IRQ 番号は 35 始まり +/* platform/linux/platform.h +#define INTR_IRQ_BASE (SIGRTMIN+1) +*/ #define DUMMY_IRQ INTR_IRQ_BASE static int dummy_transmit(struct net_device *dev, uint16_t type, @@ -19,6 +24,10 @@ static int dummy_transmit(struct net_device *dev, uint16_t type, // データを破棄 // ダミーデバイスなのでデータに対しては何もしない + + // テスト用に割り込みを発生させる + intr_raise_irq(DUMMY_IRQ); + return 0; } @@ -28,7 +37,15 @@ static struct net_device_ops dummy_ops = { .transmit = dummy_transmit, }; -static int dummy_isr(unsigned int irq, void *id) {} +static int dummy_isr(unsigned int irq, void *id) { + // 現段階ではダミーデバイス用の割り込みハンドラが呼び出されたことがわかれば良いのでデバッグ出力のみ + // FIXME: 本来は net_device 以外にも対応するため、キャストは行わない。 + // あくまで現段階ではデバッグ用 + struct net_device *dev = (struct net_device *)id; // 無理やりキャスト + debugf("ISR called: irq=%u, dev=%s", irq, dev->name); + + return 0; +} /* ダミーデバイスの初期化 @@ -66,5 +83,10 @@ struct net_device *dummy_init(void) { } debugf("dummy device initialized: dev=%s", dev->name); + // デバイスの割り込みハンドラとして dummy_isr を登録する + infof("register interrupt handler for dummy device"); + intr_register_irq_entry(DUMMY_IRQ, dummy_isr, INTR_IRQ_SHARED, dev->name, + dev); + return dev; } From 7cbefd4cad7ddf66f21d9458eb50998ee69a5ead Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:30:05 +0900 Subject: [PATCH 32/36] feat: running interrupt processing on protocol stack (on `net_init`, `net_run`, `net_shutdown`) --- net.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/net.c b/net.c index 515388e5..2e75d5db 100644 --- a/net.c +++ b/net.c @@ -205,6 +205,14 @@ int main(void) { int net_run(void) { struct net_device *dev; + // 割り込み機構の起動 + debugf("start interrupt processing..."); + if (intr_run() == -1) { + errorf("intr_run() failure"); + return -1; + } + + // 登録済みの全デバイスをオープン debugf("open all devices..."); for (dev = devices; dev; dev = dev->next) { if (net_device_open(dev) == -1) { @@ -221,6 +229,7 @@ int net_run(void) { void net_shutdown(void) { struct net_device *dev; + // 登録済みの全デバイスをクローズ debugf("close all devices..."); for (dev = devices; dev; dev = dev->next) { if (net_device_close(dev) == -1) { @@ -228,11 +237,22 @@ void net_shutdown(void) { // 失敗しても for 文を抜けはしない } } + + // 割り込み機構の終了 + debugf("shutdown interrupt processing..."); + intr_shutdown(); + debugf("shutting down"); } int net_init(void) { - // TODO: 今は何もしない。後に処理を追記。 + // 割り込み機構の初期化 + debugf("initialize interrupt processing..."); + if (intr_init() == -1) { + errorf("intr_init() failure"); + return -1; + } + infof("initialized protocol stack"); return 0; From fbc0200cbbb121c03f172e0885012271cf97c6a8 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:30:33 +0900 Subject: [PATCH 33/36] feat: add test for interrupt processing --- test/step2.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/step2.c diff --git a/test/step2.c b/test/step2.c new file mode 100644 index 00000000..d30bb589 --- /dev/null +++ b/test/step2.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "net.h" +#include "util.h" + +#include "driver/dummy.h" + +#include "test.h" + +static volatile sig_atomic_t terminate; + +/* シグナルハンドラ + +原則、シグナルハンドラの中では下記以外のことはしない: + +- 非同期安全な関数の呼び出し + - ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html +- volatile sig_atomic_t 型の変数への書き込み +*/ +static void on_signal(int signal) { + /* + 一般に、I/O 関数をシグナルハンドラ内で呼び出すのは安全ではない。 + シグナルハンドラ内で I/O 関数を使用する場合は、 + 使用するシステムの非同期安全な関数を事前に確認すること。 + */ + // warnf("on_signal: signal received %d", signal); + + /* `(void)signal;` は必要? + `(void)signal;` は「この引数は使っていない」ことを意図的に示す + ためのお決まりの書き方。多くのコンパイラは未使用引数に警告を出すので、 + それを抑止する目的で入れている。 + + - 既にハンドラ内で `signal` を使って何かするなら不要。 + - 使わないならそのまま残しておけば警告を避けられる。 + - 別の書き方として、 + - `__attribute__((unused))` + - `void on_signal(int \/\*signal\*\/)` + - のようにコメントで示す例もある。 + - 単に `(void)signal;` がもっとも広く使われる方法です。 + */ + + // (void)signal; + terminate = 1; +} + +int main(int argc, char *argv[]) { + struct net_device *dev; + + // シグナルハンドラの設定 + // Ctrl + C が押された際に行儀よく終了するようにする + signal(SIGINT, on_signal); + + // プロトコルスタックの初期化 + if (net_init() == -1) { + errorf("net_init() failure"); + return -1; + } + + // ダミーデバイスの初期化(ダミーデバイスの生成、設定、登録) + dev = dummy_init(); + + // プロトコルスタックの起動 + if (net_run() == -1) { + errorf("net_run() failure"); + return -1; + } + + // 1 秒おきにデバイスにパケットを書き込む + // まだパケットを自力で生成できないのでテストデータを用いる + uint16_t type = 0x0800; + size_t len = sizeof(test_data); + // Ctrl + C が押されるとシグナルハンドラ on_signal() の中で + // terminate に 1 が設定され、while ループを抜ける + while (!terminate) { + // Internet Layer -> Network Interface Layer への書き込み + // Internet Layer から来たパケットをデバイスに書き込む + if (net_device_output(dev, type, test_data, len, NULL) == -1) { + errorf("net_device_output() failure"); + break; + } + + sleep(1); + } + + // プロトコルスタックの停止 + net_shutdown(); + + return 0; +} From 42b6ceefe6f35f548cdf44cb059fb03b2afa2c99 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:30:59 +0900 Subject: [PATCH 34/36] chore: add build target for interrupt processing --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index cfb63439..af06d668 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ OBJS = util.o \ TESTS = test/step0.exe \ test/step1.exe \ + test/step2.exe \ CFLAGS := $(CFLAGS) -g -W -Wall -Wno-unused-parameter -iquote . @@ -14,6 +15,7 @@ ifeq ($(shell uname),Linux) # Linux specific settings BASE = platform/linux CFLAGS := $(CFLAGS) -pthread -iquote $(BASE) + OBJS := $(OBJS) $(BASE)/intr.o endif ifeq ($(shell uname),Darwin) From 8333b466d9e574b0bdc40ee695df5ffa16fb8cd7 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:31:23 +0900 Subject: [PATCH 35/36] docs: add empty architecture diagram of Excalidraw --- docs/microps-architecture.excalidraw | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/microps-architecture.excalidraw diff --git a/docs/microps-architecture.excalidraw b/docs/microps-architecture.excalidraw new file mode 100644 index 00000000..401338e7 --- /dev/null +++ b/docs/microps-architecture.excalidraw @@ -0,0 +1,14 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff", + "lockedMultiSelections": {} + }, + "files": {} +} \ No newline at end of file From 878958597ecd644ea84f9952e5180aa5b03d2bf4 Mon Sep 17 00:00:00 2001 From: nukopy Date: Sun, 28 Sep 2025 01:34:25 +0900 Subject: [PATCH 36/36] DONE Day 1 - STEP 2: Interrupt Processing :tada: