| 1 | /* @title: Error Codes */ |
| 2 | #pragma once |
| 3 | #include <compiler.h> |
| 4 | #include <console/panic.h> |
| 5 | #include <kassert.h> |
| 6 | #include <linker/symbols.h> |
| 7 | #include <stdarg.h> |
| 8 | #include <stdint.h> |
| 9 | |
| 10 | #define ERR_IS_FATAL(e) (e != ERR_OK && e != ERR_AGAIN) |
| 11 | |
| 12 | #define ERR_IS_MATCH_1(v, a) ((v) == (a)) |
| 13 | #define ERR_IS_MATCH_2(v, a, b) (ERR_IS_MATCH_1(v, a) || ((v) == (b))) |
| 14 | #define ERR_IS_MATCH_3(v, a, b, c) (ERR_IS_MATCH_2(v, a, b) || ((v) == (c))) |
| 15 | #define ERR_IS_MATCH_4(v, a, b, c, d) \ |
| 16 | (ERR_IS_MATCH_3(v, a, b, c) || ((v) == (d))) |
| 17 | #define ERR_IS_MATCH_5(v, a, b, c, d, e) \ |
| 18 | (ERR_IS_MATCH_4(v, a, b, c, d) || ((v) == (e))) |
| 19 | #define ERR_IS_MATCH_6(v, a, b, c, d, e, f) \ |
| 20 | (ERR_IS_MATCH_5(v, a, b, c, d, e) || ((v) == (f))) |
| 21 | #define ERR_IS_MATCH_7(v, a, b, c, d, e, f, g) \ |
| 22 | (ERR_IS_MATCH_6(v, a, b, c, d, e, f) || ((v) == (g))) |
| 23 | #define ERR_IS_MATCH_8(v, a, b, c, d, e, f, g, h) \ |
| 24 | (ERR_IS_MATCH_7(v, a, b, c, d, e, f, g) || ((v) == (h))) |
| 25 | |
| 26 | #define ERR_GUARD(val, ...) \ |
| 27 | ({ \ |
| 28 | __auto_type _v = (val); \ |
| 29 | if (unlikely(_v < 0 && !_DISPATCH(ERR_IS_MATCH, PP_NARG(__VA_ARGS__))( \ |
| 30 | _v, __VA_ARGS__))) \ |
| 31 | panic("unhandled error: %s", errno_to_str(_v)); \ |
| 32 | _v; \ |
| 33 | }) |
| 34 | |
| 35 | /* When this enum errno is negated, i.e. becomes positive, the upper 16 bits |
| 36 | * indicate the *facility*. With 0, it's just one of these, but if it's > 0, |
| 37 | * it came from a specific subsystem that defined a struct err_facility */ |
| 38 | enum errno { |
| 39 | ERR_OK = 0, // Success |
| 40 | ERR_UNKNOWN = -1, // Unknown or unspecified error |
| 41 | ERR_NO_MEM = -2, // Out of memory |
| 42 | ERR_NO_DEV = -3, // No such device |
| 43 | ERR_NO_ENT = -4, // No such file or directory |
| 44 | ERR_EXIST = -5, // File already exists |
| 45 | ERR_IO = -6, // I/O error |
| 46 | ERR_NOT_DIR = -7, // Not a directory |
| 47 | ERR_IS_DIR = -8, // Is a directory |
| 48 | ERR_INVAL = -9, // Invalid argument |
| 49 | ERR_PERM = -10, // Permission denied |
| 50 | ERR_FAULT = -11, // Bad address |
| 51 | ERR_BUSY = -12, // Resource/device busy |
| 52 | ERR_AGAIN = -13, // Try again later |
| 53 | ERR_NOT_IMPL = -14, // Not implemented |
| 54 | ERR_NOSPC = -15, // No space left on device |
| 55 | ERR_OVERFLOW = -16, // Value too large |
| 56 | ERR_NOT_EMPTY = -17, // Directory not empty |
| 57 | |
| 58 | }; |
| 59 | |
| 60 | struct err_facility { |
| 61 | uint16_t prefix; |
| 62 | const char *name; |
| 63 | const char *desc; |
| 64 | const char *(*const to_str)(uint16_t delta); |
| 65 | }; |
| 66 | |
| 67 | /* Deltas must always be positive */ |
| 68 | #define ERR_CREATE(pre, del) \ |
| 69 | ({ \ |
| 70 | kassert((del & 0xFFFF) == del); \ |
| 71 | kassert(del > 0); \ |
| 72 | -((((int) (pre)) << 16) | (((int) (del)) & 0xFFFF)); \ |
| 73 | }) |
| 74 | |
| 75 | #define ERR_GET_FACILITY(e) \ |
| 76 | ({ \ |
| 77 | kassert(e < 0); \ |
| 78 | ((-(e)) >> 16) & 0xFFFF; \ |
| 79 | }) |
| 80 | |
| 81 | #define ERR_GET_DELTA(e) \ |
| 82 | ({ \ |
| 83 | kassert(e < 0); \ |
| 84 | ((-(e)) & 0xFFFF); \ |
| 85 | }) |
| 86 | |
| 87 | #define ERR_PREFIX(n) ((__err_facility_##n).prefix) |
| 88 | #define ERR_DELTA_START (1) |
| 89 | #define ERR(n, d) ERR_CREATE(ERR_PREFIX(n), d) |
| 90 | |
| 91 | #define ERR_FACILITY(n) __err_facility_##n |
| 92 | #define ERR_FACILITY_EXTERN(n) extern struct err_facility __err_facility_##n |
| 93 | #define ERR_FACILITY_DECLARE(n, ...) \ |
| 94 | LINKER_SECTION_OBJECT(struct err_facility, err_facilities) \ |
| 95 | __err_facility_##n = {.name = #n, __VA_ARGS__} |
| 96 | |
| 97 | LINKER_SECTION_DEFINE(struct err_facility, err_facilities); |
| 98 | |
| 99 | const char *errno_facility_to_str(enum errno err); |
| 100 | void err_facilities_init(); |
| 101 | |
| 102 | static inline const char *errno_to_str(enum errno err) { |
| 103 | switch (err) { |
| 104 | case ERR_OK: return "No error" ; |
| 105 | case ERR_UNKNOWN: return "Unknown error" ; |
| 106 | case ERR_NO_MEM: return "Out of memory" ; |
| 107 | case ERR_NO_DEV: return "No such device" ; |
| 108 | case ERR_NO_ENT: return "No such file or directory" ; |
| 109 | case ERR_EXIST: return "File already exists" ; |
| 110 | case ERR_IO: return "I/O error" ; |
| 111 | case ERR_NOT_DIR: return "Not a directory" ; |
| 112 | case ERR_IS_DIR: return "Is a directory" ; |
| 113 | case ERR_INVAL: return "Invalid argument" ; |
| 114 | case ERR_PERM: return "Permission denied" ; |
| 115 | case ERR_FAULT: return "Bad memory access" ; |
| 116 | case ERR_BUSY: return "Resource busy" ; |
| 117 | case ERR_AGAIN: return "Try again" ; |
| 118 | case ERR_NOT_IMPL: return "Not implemented" ; |
| 119 | case ERR_NOSPC: return "No space left" ; |
| 120 | case ERR_OVERFLOW: return "Value too large" ; |
| 121 | case ERR_NOT_EMPTY: return "Directory not empty" ; |
| 122 | |
| 123 | default: return errno_facility_to_str(err); |
| 124 | } |
| 125 | } |
| 126 | |