├── .gitignore ├── Framebuffer.ml ├── Framebuffer_stubs.c ├── LICENSE ├── Makefile ├── README.md ├── Thread.ml ├── Thread_stubs.c ├── Time.ml ├── Time_stubs.c ├── boot.S ├── entry.S ├── foo.ml ├── link-arm-eabi.ld ├── list.h ├── main.c ├── memory.c ├── memory.h ├── mmio.h ├── printf.c ├── printf.h ├── string.c ├── string.h ├── test ├── list.c └── memory.c ├── uart.c └── uart.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | *.i*86 22 | *.x86_64 23 | *.hex 24 | *.img 25 | *.symbols 26 | test/list 27 | test/memory 28 | 29 | # Ocaml files 30 | *.cmi 31 | *.cmo 32 | *.cmx 33 | 34 | # Depends files 35 | *.d 36 | 37 | # Backup files 38 | *~ 39 | -------------------------------------------------------------------------------- /Framebuffer.ml: -------------------------------------------------------------------------------- 1 | (* Framebuffer.ml - ocaml framebuffer interface 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Access to the Raspberry Pi framebuffer 20 | *) 21 | 22 | type result = 23 | | SUCCESS 24 | (* Error codes *) 25 | | FAIL_GET_RESOLUTION 26 | | FAIL_GOT_INVALID_RESOLUTION 27 | | FAIL_SETUP_FRAMEBUFFER 28 | | FAIL_INVALID_TAGS 29 | | FAIL_INVALID_TAG_RESPONSE 30 | | FAIL_INVALID_TAG_DATA 31 | | FAIL_INVALID_PITCH_RESPONSE 32 | | FAIL_INVALID_PITCH_DATA 33 | 34 | let to_string = function 35 | | SUCCESS -> "SUCCESS" 36 | (* Error codes *) 37 | | FAIL_GET_RESOLUTION -> "FAIL_GET_RESOLUTION" 38 | | FAIL_GOT_INVALID_RESOLUTION -> "FAIL_GOT_INVALID_RESOLUTION" 39 | | FAIL_SETUP_FRAMEBUFFER -> "FAIL_SETUP_FRAMEBUFFER" 40 | | FAIL_INVALID_TAGS -> "FAIL_INVALID_TAGS" 41 | | FAIL_INVALID_TAG_RESPONSE -> "FAIL_INVALID_TAG_RESPONSE" 42 | | FAIL_INVALID_TAG_DATA -> "FAIL_INVALID_TAG_DATA" 43 | | FAIL_INVALID_PITCH_RESPONSE -> "FAIL_INVALID_PITCH_RESPONSE" 44 | | FAIL_INVALID_PITCH_DATA -> "FAIL_INVALID_PITCH_DATA" 45 | 46 | external init : unit -> result = "ocaml_rpi__fb_init" 47 | 48 | let () = 49 | let res = init () 50 | in 51 | Printf.printf "Framebuffer.init () -> %s\n%!" (to_string res) 52 | -------------------------------------------------------------------------------- /Framebuffer_stubs.c: -------------------------------------------------------------------------------- 1 | /* Framebuffer_stubs.ml - stubs for the ocaml framebuffer interface 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Glue code to access the Raspberry Pi framebuffer from ocaml 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "printf.h" 26 | 27 | /* 28 | * Data memory barrier 29 | * No memory access after the DMB can run until all memory accesses 30 | * before it have completed. 31 | */ 32 | static inline void data_memory_barrier(void) { 33 | asm volatile("mcr p15, #0, %[zero], c7, c10, #5" 34 | : : [zero]"r"(0)); 35 | } 36 | 37 | /* 38 | * Clean and invalidate entire cache 39 | * Flush pending writes to main memory 40 | * Remove all data in data cache 41 | */ 42 | static inline void flush_cache(void) { 43 | asm volatile("mcr p15, #0, %[zero], c7, c14, #0" 44 | : : [zero]"r"(0)); 45 | } 46 | 47 | enum { 48 | // Mailbox registers 49 | MAILBOX0READ = 0xE000b880, 50 | MAILBOX0STATUS = 0xE000b898, 51 | MAILBOX0WRITE = 0xE000b8a0, 52 | }; 53 | 54 | // Mailbox status 55 | volatile uint32_t *fb_mailbox0_status = (uint32_t*)MAILBOX0STATUS; 56 | 57 | enum StatusFlags { 58 | MAIL_EMPTY = 1 << 30, 59 | MAIL_FULL = 1 << 31 60 | }; 61 | 62 | int mail_empty(void) { 63 | return *fb_mailbox0_status & MAIL_EMPTY; 64 | } 65 | 66 | int mail_full(void) { 67 | return *fb_mailbox0_status & MAIL_FULL; 68 | } 69 | 70 | // Envelope 71 | typedef struct Envelope { 72 | uint32_t raw; 73 | } Envelope; 74 | 75 | enum { 76 | ENVELOPE_SHIFT_CHANNEL = 0, ENVELOPE_SHIFT_ADDR = 4, 77 | ENVELOPE_MASK_CHANNEL = 0xF, ENVELOPE_MASK_ADDR = 0xFFFFFFF0 78 | }; 79 | 80 | Envelope envelope(uint32_t chan, volatile uint32_t *address) { 81 | return (Envelope){ (intptr_t)address | chan }; 82 | } 83 | 84 | Envelope envelope_raw(uint32_t raw) { 85 | return (Envelope){ raw }; 86 | } 87 | 88 | uint32_t envelope_channel(const Envelope e) { 89 | return (e.raw & ENVELOPE_MASK_CHANNEL) >> ENVELOPE_SHIFT_CHANNEL; 90 | } 91 | 92 | volatile uint32_t * envelope_addr(const Envelope e) { 93 | return (volatile uint32_t *)(e.raw & ENVELOPE_MASK_ADDR); 94 | } 95 | 96 | // Mailbox read 97 | volatile uint32_t *fb_mailbox0_read = (volatile uint32_t *)MAILBOX0READ; 98 | 99 | volatile uint32_t* mail_read(uint32_t chan) { 100 | // possibly switching peripheral 101 | data_memory_barrier(); 102 | while(1) { 103 | // don't use cached values 104 | flush_cache(); 105 | // wait for mailbox to contain something 106 | while(mail_empty()) { 107 | flush_cache(); 108 | } 109 | Envelope e = envelope_raw(*fb_mailbox0_read); 110 | if (envelope_channel(e) == chan) return envelope_addr(e); 111 | } 112 | } 113 | 114 | // Mailbox write 115 | volatile uint32_t *fb_mailbox0_write = (volatile uint32_t *)MAILBOX0WRITE; 116 | 117 | void mail_write(const Envelope e) { 118 | // possibly switching peripheral 119 | data_memory_barrier(); 120 | // don't use cached values 121 | flush_cache(); 122 | // wait for mailbox to be not full 123 | while(mail_full()) { 124 | flush_cache(); 125 | } 126 | *fb_mailbox0_write = e.raw; 127 | } 128 | 129 | // Framebuffer 130 | enum { 131 | // Channel for framebuffer 132 | FBCHAN = 8, 133 | }; 134 | 135 | /* Use some free memory in the area below the kernel/stack */ 136 | #define BUFFER_ADDRESS 0x1000 137 | 138 | enum Error { 139 | SUCCESS, 140 | // Error codes 141 | FAIL_GET_RESOLUTION, 142 | FAIL_GOT_INVALID_RESOLUTION, 143 | FAIL_SETUP_FRAMEBUFFER, 144 | FAIL_INVALID_TAGS, 145 | FAIL_INVALID_TAG_RESPONSE, 146 | FAIL_INVALID_TAG_DATA, 147 | FAIL_INVALID_PITCH_RESPONSE, 148 | FAIL_INVALID_PITCH_DATA 149 | }; 150 | 151 | typedef struct Pixel { 152 | uint8_t red; 153 | uint8_t green; 154 | uint8_t blue; 155 | uint8_t alpha; 156 | } Pixel; 157 | 158 | typedef struct FB { 159 | uint32_t base; 160 | uint32_t size; // in bytes 161 | uint32_t pitch; // in bytes 162 | uint32_t width; 163 | uint32_t height; 164 | } FB; 165 | 166 | FB fb; 167 | 168 | CAMLprim value ocaml_rpi__fb_init(value unit) { 169 | CAMLparam1(unit); 170 | printf("%s()\n", __FUNCTION__); 171 | 172 | volatile uint32_t *mailbuffer = (volatile uint32_t*)BUFFER_ADDRESS; 173 | Envelope e = envelope(FBCHAN, mailbuffer); 174 | 175 | /* Get the display size */ 176 | mailbuffer[0] = 8 * 4; // Total size 177 | mailbuffer[1] = 0; // Request 178 | mailbuffer[2] = 0x40003; // Display size 179 | mailbuffer[3] = 8; // Buffer size 180 | mailbuffer[4] = 0; // Request size 181 | mailbuffer[5] = 0; // Space for horizontal resolution 182 | mailbuffer[6] = 0; // Space for vertical resolution 183 | mailbuffer[7] = 0; // End tag 184 | 185 | mail_write(e); 186 | mail_read(FBCHAN); 187 | 188 | /* Valid response in data structure */ 189 | if (mailbuffer[1] != 0x80000000) CAMLreturn(Int_val(FAIL_GET_RESOLUTION)); 190 | 191 | fb.width = mailbuffer[5]; 192 | fb.height = mailbuffer[6]; 193 | 194 | if (fb.width == 0 || fb.height == 0) CAMLreturn(Int_val(FAIL_GOT_INVALID_RESOLUTION)); 195 | 196 | /* Set up screen */ 197 | 198 | unsigned int c = 1; 199 | mailbuffer[c++] = 0; // Request 200 | 201 | mailbuffer[c++] = 0x00048003; // Tag id (set physical size) 202 | mailbuffer[c++] = 8; // Value buffer size (bytes) 203 | mailbuffer[c++] = 8; // Req. + value length (bytes) 204 | mailbuffer[c++] = fb.width; // Horizontal resolution 205 | mailbuffer[c++] = fb.height; // Vertical resolution 206 | 207 | mailbuffer[c++] = 0x00048004; // Tag id (set virtual size) 208 | mailbuffer[c++] = 8; // Value buffer size (bytes) 209 | mailbuffer[c++] = 8; // Req. + value length (bytes) 210 | mailbuffer[c++] = fb.width; // Horizontal resolution 211 | mailbuffer[c++] = fb.height; // Vertical resolution 212 | 213 | mailbuffer[c++] = 0x00048005; // Tag id (set depth) 214 | mailbuffer[c++] = 4; // Value buffer size (bytes) 215 | mailbuffer[c++] = 4; // Req. + value length (bytes) 216 | mailbuffer[c++] = 32; // 32 bpp 217 | 218 | mailbuffer[c++] = 0x00040001; // Tag id (allocate framebuffer) 219 | mailbuffer[c++] = 8; // Value buffer size (bytes) 220 | mailbuffer[c++] = 4; // Req. + value length (bytes) 221 | mailbuffer[c++] = 16; // Alignment = 16 222 | mailbuffer[c++] = 0; // Space for response 223 | 224 | mailbuffer[c++] = 0; // Terminating tag 225 | 226 | mailbuffer[0] = c*4; // Buffer size 227 | 228 | mail_write(e); 229 | mail_read(FBCHAN); 230 | 231 | /* Valid response in data structure */ 232 | if(mailbuffer[1] != 0x80000000) CAMLreturn(Int_val(FAIL_SETUP_FRAMEBUFFER)); 233 | 234 | // Scan replies for allocate response 235 | unsigned int i = 2; /* First tag */ 236 | uint32_t data; 237 | while ((data = mailbuffer[i])) { 238 | // allocate response? 239 | if (data == 0x40001) break; 240 | 241 | /* Skip to next tag 242 | * Advance count by 1 (tag) + 2 (buffer size/value size) 243 | * + specified buffer size 244 | */ 245 | i += 3 + (mailbuffer[i + 1] >> 2); 246 | 247 | if (i > c) CAMLreturn(Int_val(FAIL_INVALID_TAGS)); 248 | } 249 | 250 | /* 8 bytes, plus MSB set to indicate a response */ 251 | if (mailbuffer[i + 2] != 0x80000008) CAMLreturn(Int_val(FAIL_INVALID_TAG_RESPONSE)); 252 | 253 | /* Framebuffer address/size in response */ 254 | fb.base = mailbuffer[i + 3]; 255 | fb.size = mailbuffer[i + 4]; 256 | 257 | if (fb.base == 0 || fb.size == 0) CAMLreturn(Int_val(FAIL_INVALID_TAG_DATA)); 258 | 259 | fb.base += 0xC0000000; // physical to virtual 260 | 261 | /* Get the framebuffer pitch (bytes per line) */ 262 | mailbuffer[0] = 7 * 4; // Total size 263 | mailbuffer[1] = 0; // Request 264 | mailbuffer[2] = 0x40008; // Display size 265 | mailbuffer[3] = 4; // Buffer size 266 | mailbuffer[4] = 0; // Request size 267 | mailbuffer[5] = 0; // Space for pitch 268 | mailbuffer[6] = 0; // End tag 269 | 270 | mail_write(e); 271 | mail_read(FBCHAN); 272 | 273 | /* 4 bytes, plus MSB set to indicate a response */ 274 | if (mailbuffer[4] != 0x80000004) CAMLreturn(Int_val(FAIL_INVALID_PITCH_RESPONSE)); 275 | 276 | fb.pitch = mailbuffer[5]; 277 | if (fb.pitch == 0) CAMLreturn(Int_val(FAIL_INVALID_PITCH_DATA)); 278 | 279 | // set alpha to 0xff everywhere 280 | for(uint32_t n = 3; n < fb.size; n += 4) { 281 | *(uint8_t*)(fb.base + n) = 0xff; 282 | } 283 | 284 | // draw chessboard pattern 285 | for(uint32_t y = 0; y < fb.height; ++y) { 286 | for(uint32_t x = 0; x < fb.width; ++x) { 287 | Pixel *p = (Pixel*)(fb.base + x * sizeof(Pixel) + y * fb.pitch); 288 | uint8_t col = ((x & 16) ^ (y & 16)) ? 0x00 : 0xff; 289 | p->red = col; 290 | p->green = col; 291 | p->blue = col; 292 | } 293 | } 294 | 295 | // draw back->red fade left to right at the top 296 | // draw back->blue fade left to right at the bottom 297 | for(int y = 0; y < 16; ++y) { 298 | for(int x = 16; x < 256 + 16; ++x) { 299 | Pixel *p = (Pixel*)(fb.base + x * sizeof(Pixel) + y * fb.pitch); 300 | p->red = x - 16; 301 | p->green = 0; 302 | p->blue = 0; 303 | p = (Pixel*)(fb.base + x * sizeof(Pixel) + (fb.height - y - 1) * fb.pitch); 304 | p->red = 0; 305 | p->green = 0; 306 | p->blue = x - 16; 307 | } 308 | } 309 | // draw back->green fade top to bottom at the left 310 | // draw back->green fade top to bottom at the right 311 | for(int y = 16; y < 256 + 16; ++y) { 312 | for(int x = 0; x < 16; ++x) { 313 | Pixel *p = (Pixel*)(fb.base + x * sizeof(Pixel) + y * fb.pitch); 314 | p->red = 0; 315 | p->green = y - 16; 316 | p->blue = 0; 317 | p = (Pixel*)(fb.base + (fb.width - x- 1) * sizeof(Pixel) + y * fb.pitch); 318 | p->red = y - 16; 319 | p->green = y - 16; 320 | p->blue = y - 16; 321 | } 322 | } 323 | /* test font 324 | const char text[] = "MOOSE V1.0"; 325 | struct Arg { 326 | uint32_t color; 327 | uint32_t border; 328 | bool fill; 329 | } args[] = { 330 | {~0LU, 0, false}, 331 | { 0, ~0LU, false}, 332 | {~0LU, 0, true}, 333 | { 0, ~0LU, true}, 334 | {0xff0000ff, 0, false}, 335 | {0xff0000ff, ~0LU, false}, 336 | {0xff0000ff, 0, true}, 337 | {0xff0000ff, ~0LU, true}, 338 | {0xff00ff00, 0, false}, 339 | {0xff00ff00, ~0LU, false}, 340 | {0xff00ff00, 0, true}, 341 | {0xff00ff00, ~0LU, true}, 342 | {0xffff0000, 0, false}, 343 | {0xffff0000, ~0LU, false}, 344 | {0xffff0000, 0, true}, 345 | {0xffff0000, ~0LU, true}, 346 | }; 347 | int y = 152; 348 | Arg *arg = args; 349 | for (i = 0; i < 16; ++i) { 350 | int x = 152; 351 | for (const char *p = text; *p; ++p) { 352 | Font::putc(fb, x, y, *p, arg->color, arg->border, arg->fill); 353 | x += 8; 354 | } 355 | y += 16; 356 | ++arg;} 357 | */ 358 | CAMLreturn(Int_val(SUCCESS)); 359 | } 360 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEPENDFLAGS := -MD -MP 2 | INCLUDES := -I . 3 | BASEFLAGS := -O2 -fpic -nostdlib -std=gnu99 4 | BASEFLAGS += -ffreestanding -fomit-frame-pointer 5 | BASEFLAGS += -D_FILE_OFFSET_BITS=64 6 | CPUFLAGS := -mcpu=arm1176jzf-s -marm -mhard-float -mfpu=vfp 7 | WARNFLAGS := -Wall -Wextra -Wshadow -Wcast-align -Wwrite-strings 8 | WARNFLAGS += -Wredundant-decls -Winline 9 | WARNFLAGS += -Wno-endif-labels -Wfloat-equal 10 | WARNFLAGS += -Wformat=2 -Winit-self 11 | WARNFLAGS += -Winvalid-pch -Wmissing-format-attribute 12 | WARNFLAGS += -Wmissing-include-dirs 13 | WARNFLAGS += -Wredundant-decls -Wshadow 14 | WARNFLAGS += -Wswitch -Wsystem-headers 15 | WARNFLAGS += -Wno-pragmas 16 | WARNFLAGS += -Wno-psabi 17 | WARNFLAGS += -Wwrite-strings -Wdisabled-optimization -Wpointer-arith 18 | WARNFLAGS += -Werror -Wno-error=unused-variable -Wno-error=unused-parameter 19 | WARNFLAGS += -Wno-error=unused-function 20 | ASFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(CPUFLAGS) -D__ASSEMBLY__ 21 | ALLFLAGS := $(BASEFLAGS) $(WARNFLAGS) $(INCLUDES) $(DEPENDFLAGS) 22 | ALLFLAGS += $(CPUFLAGS) 23 | 24 | CFLAGS := $(ALLFLAGS) -Wstrict-prototypes -Wnested-externs -Winline 25 | CXXFLAGS := $(ALLFLAGS) -fno-exceptions -std=c++11 26 | LDFLAGS := $(BASEFLAGS) 27 | 28 | CC := gcc 29 | OBJCOPY := objcopy 30 | OBJDUMP := objdump 31 | 32 | all: kernel.img kernel.symbols tests 33 | 34 | # Basic patterns 35 | %.o: %.S 36 | $(CC) $(ASFLAGS) -MT $@ -MF $@.d -c $< -o $@ 37 | 38 | %.o: %.c 39 | $(CC) $(CFLAGS) -MT $@ -MF $@.d -c $< -o $@ 40 | 41 | ocaml.o: Thread.ml Time.ml Framebuffer.ml foo.ml 42 | # ocamlopt -output-obj -o $@ -thread unix.cmxa threads.cmxa $+ 43 | ocamlopt -output-obj -o $@ $+ 44 | 45 | kernel.elf: boot.o entry.o uart.o printf.o string.o memory.o main.o Thread_stubs.o Time_stubs.o Framebuffer_stubs.o ocaml.o 46 | # $(CC) -nostdlib -ffreestanding -o $@ $+ -L/usr/lib/ocaml -lasmrun 47 | # $(CC) -o $@ $+ -L/usr/lib/ocaml -lasmrun 48 | $(CC) $(LDFLAGS) -Tlink-arm-eabi.ld -o $@ $+ -L/usr/lib/ocaml -lasmrun -lunix -L . -lgcc 49 | 50 | %.img: %.elf 51 | $(OBJCOPY) -R .bss $< -O binary $@ 52 | 53 | %.symbols: %.elf 54 | $(OBJDUMP) -t $< | sort >$@ 55 | 56 | clean: 57 | rm -f *.o *.cmx *.cmi *.elf *.img *.symbols *~ 58 | rm -f test/list test/memory 59 | 60 | # Include depends 61 | include $(wildcard *.d) $(wildcard test/*.d) 62 | 63 | QEMU = ../../qemu/install/bin/qemu-system-arm 64 | test: 65 | $(QEMU) -kernel kernel.elf -initrd kernel.elf -cpu arm1176 -m 512 -M raspi -serial stdio -device usb-kbd 66 | 67 | tests: test/list test/memory 68 | 69 | test/%: test/%.c 70 | $(CC) -std=gnu99 -O2 -W -Wall -Wextra -Werror -g -MD -MP -MT $@ -MF $@.d -o $@ $< 71 | 72 | .PHONY: test 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ocaml-rpi 2 | ========= 3 | 4 | Exokernel to run ocaml baremetal on the Raspberry Pi 5 | 6 | 7 | To test run make and copy the kerne.img to your SD card in place of 8 | the linux kernel you normaly have on a RPi bootable card. 9 | 10 | Or compile qemu with RPi patches [1], adjust the QEMU variable in the 11 | Makefile and run "make && make test". 12 | 13 | -- 14 | [1] https://github.com/Torlus/qemu.git 15 | -------------------------------------------------------------------------------- /Thread.ml: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | external init : unit -> unit = "ocaml_thread_init" 4 | 5 | let () = init () 6 | 7 | external create : (unit -> unit) -> t = "caml_thread_create" 8 | external yield : unit -> unit = "schedule" 9 | external signal : int -> unit = "caml_thread_signal" 10 | -------------------------------------------------------------------------------- /Thread_stubs.c: -------------------------------------------------------------------------------- 1 | #include "printf.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define THREAD_STACK_SIZE 1024*1024 7 | #define UNUSED(x) (void)(x) 8 | 9 | /* The infos on threads (allocated via malloc()) */ 10 | 11 | struct caml_thread_struct { 12 | struct caml_thread_struct * next; /* Double linking of running threads */ 13 | struct caml_thread_struct * prev; 14 | char * top_of_stack; /* Top of stack for this thread (approx.) */ 15 | char * bottom_of_stack; /* Saved value of caml_bottom_of_stack */ 16 | uintnat last_retaddr; /* Saved value of caml_last_return_address */ 17 | value * gc_regs; /* Saved value of caml_gc_regs */ 18 | char * exception_pointer; /* Saved value of caml_exception_pointer */ 19 | struct caml__roots_block * local_roots; /* Saved value of local_roots */ 20 | int backtrace_pos; /* Saved backtrace_pos */ 21 | code_t * backtrace_buffer; /* Saved backtrace_buffer */ 22 | value backtrace_last_exn; /* Saved backtrace_last_exn (root) */ 23 | 24 | void *stack; 25 | }; 26 | 27 | typedef struct caml_thread_struct * caml_thread_t; 28 | 29 | /* The descriptor for the currently executing thread */ 30 | static caml_thread_t curr_thread = NULL; 31 | 32 | /* Hook for scanning the stacks of the other threads */ 33 | 34 | typedef void (*scanning_action) (value, value *); 35 | static void (*prev_scan_roots_hook) (scanning_action); 36 | extern void (*scan_roots_hook) (scanning_action); 37 | extern void (*enter_blocking_section_hook) (void); 38 | extern void (*leave_blocking_section_hook) (void); 39 | 40 | extern char *caml_bottom_of_stack; 41 | extern uintnat caml_last_return_address; 42 | extern value * caml_gc_regs; 43 | extern char * caml_exception_pointer; 44 | extern int caml_backtrace_pos; 45 | extern code_t * caml_backtrace_buffer; 46 | extern value caml_backtrace_last_exn; 47 | 48 | #define CRASH asm volatile("mov r11, #0xfffffff0; bx r11") 49 | 50 | /* 51 | void switch_stack(void ** old_stack_p, void * new_stack) { 52 | register void ** r0 asm("r0") = old_stack_p; 53 | register void * r1 asm("r1") = new_stack; 54 | asm volatile("push {r2-r12,r14}"); 55 | asm volatile("str sp, [%[old_stack_p], #0]" 56 | : : [old_stack_p]"r"(r0)); 57 | asm volatile("mov sp, %[new_stack]" 58 | : : [new_stack]"r"(r1)); 59 | asm volatile("pop {r2-r12,r14}"); 60 | 61 | } 62 | */ 63 | extern void switch_thread(void ** old_stack_p, void * new_stack); 64 | extern void starter_stub(caml_thread_t thread, value fn); 65 | 66 | void schedule(void) { 67 | printf("# schedule()\n"); 68 | if (curr_thread && (curr_thread != curr_thread->next)) { 69 | /* Save the stack-related global variables in the thread descriptor 70 | of the current thread */ 71 | curr_thread->bottom_of_stack = caml_bottom_of_stack; 72 | curr_thread->last_retaddr = caml_last_return_address; 73 | curr_thread->gc_regs = caml_gc_regs; 74 | curr_thread->exception_pointer = caml_exception_pointer; 75 | curr_thread->local_roots = local_roots; 76 | curr_thread->backtrace_pos = backtrace_pos; 77 | curr_thread->backtrace_buffer = backtrace_buffer; 78 | curr_thread->backtrace_last_exn = backtrace_last_exn; 79 | 80 | // switch threads 81 | printf("# switching: old_stack = %p, new_stack = %p\n", curr_thread->stack, curr_thread->next->stack); 82 | switch_thread(&curr_thread->stack, curr_thread->next->stack); 83 | printf("# switched: old_stack = %p, new_stack = %p\n", curr_thread->stack, curr_thread->next->stack); 84 | curr_thread = curr_thread->next; 85 | 86 | /* Load the stack-related global variables in the thread descriptor 87 | of the current thread */ 88 | caml_bottom_of_stack = curr_thread->bottom_of_stack; 89 | caml_last_return_address = curr_thread->last_retaddr; 90 | caml_gc_regs = curr_thread->gc_regs; 91 | caml_exception_pointer = curr_thread->exception_pointer; 92 | local_roots = curr_thread->local_roots; 93 | backtrace_pos = curr_thread->backtrace_pos; 94 | backtrace_buffer = curr_thread->backtrace_buffer; 95 | backtrace_last_exn = curr_thread->backtrace_last_exn; 96 | } 97 | } 98 | 99 | CAMLextern void caml_do_local_roots(scanning_action f, char * bottom_of_stack, 100 | uintnat last_retaddr, value * gc_regs, 101 | struct caml__roots_block * local_roots); 102 | 103 | static void caml_thread_scan_roots(scanning_action action) 104 | { 105 | // printf("# ocaml_thread_scan_roots()\n"); 106 | caml_thread_t th; 107 | 108 | th = curr_thread; 109 | do { 110 | // printf("# thread @ %p\n", th); 111 | // (*action)(th->descr, &th->descr); 112 | (*action)(th->backtrace_last_exn, &th->backtrace_last_exn); 113 | /* Don't rescan the stack of the current thread, it was done already */ 114 | if (th != curr_thread) { 115 | if (th->bottom_of_stack != NULL) { 116 | // printf("# with local roots\n"); 117 | // printf("# bottom_of_stack = %p, last_retaddr = %x, gc_regs = %p, local_roots = %p\n", 118 | // th->bottom_of_stack, (uint32_t)th->last_retaddr, 119 | // th->gc_regs, th->local_roots); 120 | do_local_roots(action, th->bottom_of_stack, th->last_retaddr, 121 | th->gc_regs, th->local_roots); 122 | } 123 | } 124 | th = th->next; 125 | } while (th != curr_thread); 126 | /* Hook */ 127 | if (prev_scan_roots_hook != NULL) (*prev_scan_roots_hook)(action); 128 | } 129 | 130 | /* Hooks for enter_blocking_section and leave_blocking_section */ 131 | 132 | static void caml_thread_enter_blocking_section(void) 133 | { 134 | printf("# caml_thread_enter_blocking_section()\n"); 135 | } 136 | 137 | static void caml_thread_leave_blocking_section(void) 138 | { 139 | printf("# caml_thread_leave_blocking_section()\n"); 140 | // schedule(); 141 | } 142 | 143 | 144 | /* Hooks for I/O locking */ 145 | typedef off_t file_offset; 146 | #ifndef IO_BUFFER_SIZE 147 | #define IO_BUFFER_SIZE 4096 148 | #endif 149 | struct channel { 150 | int fd; /* Unix file descriptor */ 151 | file_offset offset; /* Absolute position of fd in the file */ 152 | char * end; /* Physical end of the buffer */ 153 | char * curr; /* Current position in the buffer */ 154 | char * max; /* Logical end of the buffer (for input) */ 155 | void * mutex; /* Placeholder for mutex (for systhreads) */ 156 | struct channel * next, * prev;/* Double chaining of channels (flush_all) */ 157 | int revealed; /* For Cash only */ 158 | int old_revealed; /* For Cash only */ 159 | int refcount; /* For flush_all and for Cash */ 160 | int flags; /* Bitfield */ 161 | char buff[IO_BUFFER_SIZE]; /* The buffer itself */ 162 | }; 163 | extern void (*caml_channel_mutex_free)(struct channel *); 164 | extern void (*caml_channel_mutex_lock)(struct channel *); 165 | extern void (*caml_channel_mutex_unlock)(struct channel *); 166 | extern void (*caml_channel_mutex_unlock_exn)(void); 167 | struct channel * last_channel_locked = NULL; 168 | 169 | #define DEBUG_IO_MUTEX 0 170 | 171 | static void caml_io_mutex_free(struct channel *chan) { 172 | if (DEBUG_IO_MUTEX) printf("# caml_io_mutex_free(%p)\n", chan); 173 | // Nothing to do 174 | UNUSED(chan); 175 | } 176 | 177 | static int MUTEX_LOCKED = 0; 178 | static void caml_io_mutex_lock(struct channel *chan) { 179 | if (DEBUG_IO_MUTEX) printf("# caml_io_mutex_lock(%p)\n", chan); 180 | while(1) { 181 | if (chan->mutex == NULL) { 182 | chan->mutex = &MUTEX_LOCKED; 183 | last_channel_locked = chan; 184 | if (DEBUG_IO_MUTEX) printf("# caml_io_mutex_lock(%p): locked\n", chan); 185 | return; 186 | } 187 | schedule(); 188 | } 189 | } 190 | 191 | static void caml_io_mutex_unlock(struct channel *chan) { 192 | if (DEBUG_IO_MUTEX) printf("# caml_io_mutex_unlock(%p) [%p]\n", chan, chan->mutex); 193 | chan->mutex = NULL; 194 | last_channel_locked = NULL; 195 | } 196 | 197 | static void caml_io_mutex_unlock_exn(void) { 198 | if (DEBUG_IO_MUTEX) printf("# caml_io_mutex_unlock_exn(): last = %p\n", last_channel_locked); 199 | if (last_channel_locked != NULL) caml_io_mutex_unlock(last_channel_locked); 200 | } 201 | 202 | 203 | void starter(caml_thread_t th, value fn) { 204 | printf("# starter()\n"); 205 | curr_thread = th; 206 | 207 | /* Load the stack-related global variables in the thread descriptor 208 | of the current thread */ 209 | caml_bottom_of_stack = curr_thread->bottom_of_stack; 210 | caml_last_return_address = curr_thread->last_retaddr; 211 | caml_gc_regs = curr_thread->gc_regs; 212 | caml_exception_pointer = curr_thread->exception_pointer; 213 | local_roots = curr_thread->local_roots; 214 | backtrace_pos = curr_thread->backtrace_pos; 215 | backtrace_buffer = curr_thread->backtrace_buffer; 216 | backtrace_last_exn = curr_thread->backtrace_last_exn; 217 | 218 | // callback closure 219 | callback_exn(fn, Val_unit); 220 | CRASH; 221 | } 222 | 223 | // FIXME: throw exception on failure 224 | CAMLprim value caml_thread_create(value fn) 225 | { 226 | CAMLparam1(fn); 227 | caml_thread_t th; 228 | 229 | th = (caml_thread_t) malloc(sizeof(struct caml_thread_struct)); 230 | if (th != NULL) { 231 | uint32_t *stack = stat_alloc(THREAD_STACK_SIZE); 232 | if (stack == NULL) { 233 | free(th); 234 | th = NULL; 235 | } else { 236 | uint32_t *top = stack + THREAD_STACK_SIZE / sizeof(uint32_t); 237 | th->bottom_of_stack = NULL; 238 | th->top_of_stack = (char *)top; 239 | th->last_retaddr = 1; 240 | th->gc_regs = NULL; 241 | th->exception_pointer = NULL; 242 | th->local_roots = NULL; 243 | th->backtrace_pos = 0; 244 | th->backtrace_buffer = NULL; 245 | th->backtrace_last_exn = Val_unit; 246 | 247 | // Build stack frame foro starter_stub 248 | *--top = (uint32_t)starter; // LR 249 | *--top = 3; // r3 250 | *--top = 2; // r2 251 | *--top = (uint32_t)fn; // r1 252 | *--top = (uint32_t)th; // r0 253 | // Build stack frame for schedule 254 | *--top = 0; // d15 255 | *--top = 0; // d15 256 | *--top = 0; // d14 257 | *--top = 0; // d14 258 | *--top = 0; // d13 259 | *--top = 0; // d13 260 | *--top = 0; // d12 261 | *--top = 0; // d12 262 | *--top = 0; // d11 263 | *--top = 0; // d11 264 | *--top = 0; // d10 265 | *--top = 0; // d10 266 | *--top = 0; // d9 267 | *--top = 0; // d9 268 | *--top = 0; // d8 269 | *--top = 0; // d8 270 | *--top = (uint32_t)starter_stub; // LR 271 | *--top = 12; // r12 scratch 272 | *--top = 11; // r11 273 | *--top = 10; // r10 274 | *--top = 9; // r9 275 | *--top = 8; // r8 276 | *--top = 7; // r7 277 | *--top = 6; // r6 278 | *--top = 5; // r5 279 | *--top = 4; // r4 280 | 281 | th->stack = top; 282 | 283 | /* Add thread info block to the list of threads */ 284 | th->next = curr_thread->next; 285 | th->prev = curr_thread; 286 | curr_thread->next->prev = th; 287 | curr_thread->next = th; 288 | 289 | // start thread before the GC can clean up the closure 290 | schedule(); 291 | } 292 | } 293 | 294 | CAMLreturn((value)th); 295 | } 296 | 297 | // external init : unit -> unit = "ocaml_thread_init" 298 | CAMLprim value ocaml_thread_init(value unit) { 299 | CAMLparam1(unit); 300 | char c; 301 | printf("# ocaml_thread_init()\n"); 302 | /* Protect against repeated initialization (PR#1325) */ 303 | if (curr_thread != NULL) return Val_unit; 304 | 305 | /* Set up a thread info block for the current thread */ 306 | curr_thread = 307 | (caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct)); 308 | curr_thread->bottom_of_stack = NULL; 309 | curr_thread->top_of_stack = &c; 310 | curr_thread->last_retaddr = 1; 311 | curr_thread->gc_regs = NULL; 312 | curr_thread->exception_pointer = NULL; 313 | curr_thread->local_roots = NULL; 314 | curr_thread->backtrace_pos = 0; 315 | curr_thread->backtrace_buffer = NULL; 316 | curr_thread->backtrace_last_exn = Val_unit; 317 | 318 | curr_thread->next = curr_thread; 319 | curr_thread->prev = curr_thread; 320 | /* The stack-related fields will be filled in at the next 321 | enter_blocking_section */ 322 | 323 | /* Set up the hooks */ 324 | prev_scan_roots_hook = scan_roots_hook; 325 | scan_roots_hook = caml_thread_scan_roots; 326 | /* 327 | enter_blocking_section_hook = caml_thread_enter_blocking_section; 328 | leave_blocking_section_hook = caml_thread_leave_blocking_section; 329 | */ 330 | caml_channel_mutex_free = caml_io_mutex_free; 331 | caml_channel_mutex_lock = caml_io_mutex_lock; 332 | caml_channel_mutex_unlock = caml_io_mutex_unlock; 333 | caml_channel_mutex_unlock_exn = caml_io_mutex_unlock_exn; 334 | // caml_termination_hook = st_thread_exit; 335 | 336 | CAMLreturn(Val_unit); 337 | } 338 | 339 | // external signal : int -> unit = "ocaml_thread_signal" 340 | extern void caml_record_signal(int signal_number); 341 | CAMLprim value caml_thread_signal(value signal_number) { 342 | CAMLparam1(signal_number); 343 | printf("# ocaml_thread_signal(%d)\n", Int_val(signal_number)); 344 | caml_record_signal(Int_val(signal_number)); 345 | CAMLreturn(Val_unit); 346 | } 347 | 348 | -------------------------------------------------------------------------------- /Time.ml: -------------------------------------------------------------------------------- 1 | type t = { tv_sec:int; tv_usec:int; } 2 | 3 | external init : unit -> unit = "caml_time_init" 4 | external time : unit -> t = "caml_time_time" 5 | 6 | let () = init () 7 | 8 | let print time = Printf.printf "%d.%06d" time.tv_sec time.tv_usec 9 | let to_string time = Printf.sprintf "%d.%06d" time.tv_sec time.tv_usec 10 | -------------------------------------------------------------------------------- /Time_stubs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "printf.h" 3 | #include 4 | #include 5 | #include 6 | 7 | enum { 8 | // The base address for IRQs 9 | IRQ_BASE = 0xE000B200, 10 | 11 | // The offsets to reach registers for IRQs 12 | IRQ_PENDING = IRQ_BASE + 0x00, 13 | IRQ_PENDING1 = IRQ_BASE + 0x04, 14 | IRQ_PENDING2 = IRQ_BASE + 0x08, 15 | IRQ_FIQCONTROL = IRQ_BASE + 0x0C, 16 | IRQ_Enable = IRQ_BASE + 0x18, 17 | IRQ_Enable1 = IRQ_BASE + 0x10, 18 | IRQ_Enable2 = IRQ_BASE + 0x14, 19 | IRQ_Disable = IRQ_BASE + 0x24, 20 | IRQ_Disable1 = IRQ_BASE + 0x1C, 21 | IRQ_Disable2 = IRQ_BASE + 0x20, 22 | }; 23 | 24 | enum { 25 | // The base address for Timer. 26 | TIMER_BASE = 0xE0003000, 27 | 28 | // The offsets to reach registers for the TIMER. 29 | TIMER_CS = (TIMER_BASE + 0x00), 30 | TIMER_CLO = (TIMER_BASE + 0x04), 31 | TIMER_CHI = (TIMER_BASE + 0x08), 32 | TIMER_C0 = (TIMER_BASE + 0x0C), 33 | TIMER_C1 = (TIMER_BASE + 0x10), 34 | TIMER_C2 = (TIMER_BASE + 0x14), 35 | TIMER_C3 = (TIMER_BASE + 0x18), 36 | }; 37 | 38 | enum { 39 | TICKS_PER_SEC = 1000000, 40 | TICKS_PER_TOCK = 1000000, 41 | }; 42 | 43 | enum { 44 | SHIFT_MATCH0, SHIFT_MATCH1, SHIFT_MATCH2, SHIFT_MATCH3 45 | }; 46 | enum Flags { 47 | NONE, 48 | MATCH0 = 1 << SHIFT_MATCH0, 49 | MATCH1 = 1 << SHIFT_MATCH1, 50 | MATCH2 = 1 << SHIFT_MATCH2, 51 | MATCH3 = 1 << SHIFT_MATCH3, 52 | _DUMMY = 1 << 31 53 | }; 54 | 55 | // external init : unit -> unit = "ocaml_thread_init" 56 | CAMLprim value caml_time_init(value unit) { 57 | CAMLparam1(unit); 58 | printf("# ocaml_time_init()\n"); 59 | volatile uint32_t *ctrl = (uint32_t*)TIMER_CS; 60 | volatile uint32_t *lo = (uint32_t*)TIMER_CLO; 61 | volatile uint32_t *c1 = (uint32_t*)TIMER_C1; 62 | volatile uint32_t *e1 = (uint32_t*)IRQ_Enable1; 63 | *c1 = *lo + TICKS_PER_TOCK; 64 | *ctrl |= MATCH1; 65 | *e1 |= 2; // Timer 1 66 | 67 | CAMLreturn(Val_unit); 68 | } 69 | 70 | // external time : unit -> t = "caml_time_time" 71 | CAMLprim value caml_time_time(value unit) { 72 | CAMLparam1(unit); 73 | CAMLlocal1(res); 74 | uint32_t hi = *((uint32_t*)TIMER_CHI); 75 | uint32_t lo = *((uint32_t*)TIMER_CLO); 76 | uint64_t t = (((uint64_t)hi) << 32) | lo; 77 | uint32_t tv_sec = t / TICKS_PER_SEC; 78 | uint32_t tv_usec = t % TICKS_PER_SEC; 79 | res = caml_alloc_tuple(2); 80 | Store_field(res, 0, Val_int(tv_sec)); 81 | Store_field(res, 1, Val_int(tv_usec)); 82 | CAMLreturn(res); 83 | } 84 | 85 | extern void caml_record_signal(int signal_number); 86 | /* FIXME: caml_young_limit should be in r10 but sometimes that causes a crash 87 | extern char * caml_code_area_start, * caml_code_area_end, *caml_young_limit, *caml_young_end; 88 | // 32 bits: Represent page table as a 2-level array 89 | #define Pagetable2_log 11 90 | #define Pagetable2_size (1 << Pagetable2_log) 91 | #define Pagetable1_log (Page_log + Pagetable2_log) 92 | #define Pagetable1_size (1 << (32 - Pagetable1_log)) 93 | CAMLextern unsigned char * caml_page_table[Pagetable1_size]; 94 | 95 | #define Pagetable_index1(a) (((uintnat)(a)) >> Pagetable1_log) 96 | #define Pagetable_index2(a) \ 97 | ((((uintnat)(a)) >> Page_log) & (Pagetable2_size - 1)) 98 | #define Classify_addr(a) \ 99 | caml_page_table[Pagetable_index1(a)][Pagetable_index2(a)] 100 | #define In_code_area 8 101 | #define Is_in_code_area(pc) \ 102 | ( ((char *)(pc) >= caml_code_area_start && \ 103 | (char *)(pc) <= caml_code_area_end) \ 104 | || (Classify_addr(pc) & In_code_area) ) 105 | */ 106 | 107 | void time_irq_timer1(uint32_t *regs) { 108 | volatile uint32_t *ctrl = (uint32_t*)TIMER_CS; 109 | uint32_t hi = *(uint32_t*)TIMER_CHI; 110 | uint32_t lo = *(uint32_t*)TIMER_CLO; 111 | volatile uint32_t *c1 = (uint32_t*)TIMER_C1; 112 | uint32_t e = *(uint32_t*)IRQ_Enable; 113 | uint32_t e1 = *(uint32_t*)IRQ_Enable1; 114 | uint32_t e2 = *(uint32_t*)IRQ_Enable2; 115 | /* 116 | printf("### enable = %#x\n", e); 117 | printf("### enable1 = %#x\n", e1); 118 | printf("### enable2 = %#x\n", e2); 119 | printf("### ctrl = %#x\n", *ctrl); 120 | printf("### hi = %#x\n", hi); 121 | printf("### lo = %#x\n", lo); 122 | printf("### c1 = %#x\n", *c1); 123 | */ 124 | *c1 += TICKS_PER_TOCK; 125 | *ctrl |= MATCH1; 126 | // printf("regs[10] = 0x%08x, caml_young_limit = %p, caml_young_end = %p, %s\n", regs[10], caml_young_limit, caml_young_end, Is_in_code_area(regs[15])?"ocaml":"C"); 127 | caml_record_signal(0); 128 | /* FIXME: caml_young_limit should be in r10 but sometimes that causes a crash 129 | if (Is_in_code_area(regs[15])) 130 | regs[10] = (uint32_t) caml_young_limit; 131 | */ 132 | } 133 | -------------------------------------------------------------------------------- /boot.S: -------------------------------------------------------------------------------- 1 | /* boot.S - assembly startup code */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | // Mapping between virtual and physical memory 20 | #define PHYS_TO_VIRT 0xC0000000 21 | 22 | // To keep this in the first portion of the binary. 23 | .section ".text.boot" 24 | 25 | .globl Start 26 | Start: 27 | // Entry point for the kernel 28 | // r15 -> should begin execution at 0x8000. 29 | // r0 -> 0x00000000 30 | // r1 -> 0x00000C42 31 | // r2 -> 0x00000100 - start of ATAGS 32 | // preserve these registers as argument for kernel_main 33 | 34 | /**************************************************************** 35 | * Map kernel to 0xC0000000 and enable paging * 36 | ****************************************************************/ 37 | // create kernel pagetable at 0x4000 38 | mov r3, #0x4000 // kernel page table 39 | ldr r4, =(memory_regions - PHYS_TO_VIRT) // 5 regions of memory 40 | mov r5, #5 41 | 42 | .L1: // LOOP: regions of memory 43 | ldmia r4!, {r6, r7, r8} // load val, incr and count 44 | 45 | .L2: // LOOP: count 46 | mov r9, #16 // repeat entry 16 times 47 | 48 | .L3: // LOOP: 16 49 | stmia r3!, {r6} 50 | subs r9, r9, #1 // LOOP: 16 51 | bne .L3 52 | 53 | add r6, r6, r7 // val += incr 54 | subs r8, r8, #1 // LOOP: count 55 | bne .L2 56 | 57 | subs r5, r5, #1 // LOOP: regions of memory 58 | bne .L1 59 | 60 | 61 | // Translation Table Base Register 0 62 | mov r3, #0x4000 // kernel page table 63 | mcr p15, 0, r3, c2, c0, 0 // Write TTBR0 64 | 65 | // Translation Table Base Register 1 66 | mcr p15, 0, r3, c2, c0, 1 // Write TTBR1 67 | 68 | // c2, Translation Table Base Control Register 69 | ldr r4, =2 // 4K Table 0 70 | mcr p15, 0, r4, c2, c0, 2 // Write TTBCR 71 | 72 | // setup domains (CP15 c3) 73 | ldr r4, =0x55555555 // use access permissions from TLB entry 74 | mcr p15, 0, r4, c3, c0, 0 // Write Domain Access Control Register 75 | 76 | // c1 control register 77 | // 29: Force AP disabled (0) 78 | // 28: TEX remap disabled (0) 79 | // 23: Subpages AP bits disabled (1) 80 | // 13: vector base address for exceptions selected (0) 81 | // 12: enable L1 instruction cache (1) 82 | // 11: enable branch prediction (1) 83 | // 9: rom protection off (0) 84 | // 8: S bit off (0) 85 | // 2: enable data cache (1) 86 | // 1: strict alignment (1) 87 | // 0: enable MMU (1) 88 | ldr r5, =0xCF7FCBF8 89 | ldr r6, =0x00801807 90 | mrc p15, 0, r4, c1, c0, 0 // Read Control Register 91 | and r4, r4, r5 92 | orr r4, r4, r6 93 | mcr p15, 0, r4, c1, c0, 0 // Write Control Register 94 | 95 | /**************************************************************** 96 | * Switch to higher half (0xC0000000) * 97 | ****************************************************************/ 98 | ldr pc, =higher_half 99 | 100 | higher_half: 101 | // set stack for fiq mode 102 | mrs r4, cpsr // get current mode 103 | bic r4, r4, #0x1f // blank mode 104 | orr r5, r4, #0x11 // fiq mode 105 | msr cpsr_c, r5 106 | ldr sp, =_fiq_stack_end // top of stack 107 | 108 | // set stack for irq mode 109 | mrs r4, cpsr // get current mode 110 | bic r4, r4, #0x1f // blank mode 111 | orr r5, r4, #0x12 // irq mode 112 | msr cpsr_c, r5 113 | ldr sp, =_irq_stack_end // top of stack 114 | 115 | // set stack for svc mode 116 | mrs r4, cpsr // get current mode 117 | bic r4, r4, #0x1f // blank mode 118 | orr r5, r4, #0x13 // svc mode 119 | msr cpsr_c, r5 120 | ldr sp, =_svc_stack_end // top of stack 121 | 122 | // set stack for abort mode 123 | mrs r4, cpsr // get current mode 124 | bic r4, r4, #0x1f // blank mode 125 | orr r5, r4, #0x17 // abt mode 126 | msr cpsr_c, r5 127 | ldr sp, =_abt_stack_end // top of stack 128 | 129 | // set stack for undefined mode 130 | mrs r4, cpsr // get current mode 131 | bic r4, r4, #0x1f // blank mode 132 | orr r5, r4, #0x1b // und mode 133 | msr cpsr_c, r5 134 | ldr sp, =_und_stack_end // top of stack 135 | 136 | // run in system mode from here on 137 | mrs r4, cpsr // get current mode 138 | bic r4, r4, #0x1f // blank mode 139 | orr r5, r4, #0x1f // sys mode 140 | msr cpsr_c, r5 141 | 142 | // Setup the stack. 143 | ldr sp, =_stack_end // top of stack 144 | 145 | // Clear out bss. 146 | ldr r4, =_bss_start 147 | ldr r9, =_bss_end 148 | mov r5, #0 149 | mov r6, #0 150 | mov r7, #0 151 | mov r8, #0 152 | 1: // store multiple at r4. 153 | stmia r4!, {r5-r8} 154 | // If we're still below bss_end, loop. 155 | cmp r4, r9 156 | blo 1b 157 | 158 | // enable the FPU 159 | mov r5, #0 160 | mrc p15, 0, r5, c1, c0, 2 161 | orr r5, r5, #0x300000 /* single precision */ 162 | orr r5, r5, #0xC00000 /* double precision */ 163 | mcr p15, 0, r5, c1, c0, 2 164 | mov r5, #0x40000000 165 | fmxr fpexc,r5 166 | 167 | /**************************************************************** 168 | * Turn on OK LED * 169 | ****************************************************************/ 170 | // Base of GPIO registers 171 | ldr r4, =0xE0200000 172 | // enable pin 16 as output 173 | ldr r6, [r4, #4] 174 | mov r5, #1 175 | lsl r5, #18 176 | orr r6, r6, r5 177 | str r6, [r4, #4] 178 | // turn on LED 179 | mov r5, #1 180 | lsl r5, #16 181 | str r5, [r4, #40] 182 | 183 | /**************************************************************** 184 | * Call constructors * 185 | ****************************************************************/ 186 | push {r0-r2} 187 | ldr r3, =kernel_constructors 188 | blx r3 189 | pop {r0-r2} 190 | 191 | /**************************************************************** 192 | * Call kernel_main * 193 | ****************************************************************/ 194 | ldr r3, =kernel_main 195 | blx r3 196 | 197 | /**************************************************************** 198 | * Turn off OK LED * 199 | ****************************************************************/ 200 | // Base of GPIO registers 201 | ldr r4, =0xE0200000 202 | // enable pin 16 as output 203 | ldr r6, [r4, #4] 204 | mov r5, #1 205 | lsl r5, #18 206 | orr r6, r6, r5 207 | str r6, [r4, #4] 208 | // turn off LED 209 | mov r5, #1 210 | lsl r5, #16 211 | str r5, [r4, #28] 212 | 213 | // halt 214 | halt: 215 | # wfe 216 | # b halt 217 | 218 | 219 | /**************************************************************** 220 | * Blink OK LED * 221 | ****************************************************************/ 222 | // Emergency signal in case something goes horribly wrong 223 | .globl abort 224 | abort: 225 | // Base of GPIO registers 226 | ldr r4, =0xE0200000 227 | 228 | // enable pin 16 as output 229 | ldr r6, [r4, #4] 230 | mov r5, #1 231 | lsl r5, #18 232 | orr r6, r6, r5 233 | str r6, [r4, #4] 234 | 235 | 2: 236 | // turn on LED 237 | mov r5, #1 238 | lsl r5, #16 239 | str r5, [r4, #40] 240 | 241 | // wait 242 | mov r6, #0x1000000 243 | 1: 244 | sub r6, #1 245 | cmp r6, #0 246 | bne 1b 247 | 248 | // turn off LED 249 | mov r5, #1 250 | lsl r5, #16 251 | str r5, [r4, #28] 252 | 253 | // wait 254 | mov r6, #0x1000000 255 | 1: 256 | sub r6, #1 257 | cmp r6, #0 258 | bne 1b 259 | 260 | // again 261 | b 2b 262 | 263 | 264 | // switch threads 265 | .globl switch_thread 266 | switch_thread: 267 | vpush {d8-d15} 268 | push {r4-r12,r14} 269 | str sp, [r0, #0] 270 | mov sp, r1 271 | pop {r4-r12,r14} 272 | vpop {d8-d15} 273 | bx lr 274 | 275 | // starter stub 276 | .globl starter_stub 277 | starter_stub: 278 | pop {r0-r3,lr} 279 | // mov r5,$0xFFFFFFF0 280 | // bx r5 281 | bx lr 282 | 283 | 284 | // store constants 285 | constants: 286 | .ltorg 287 | 288 | .section ".data" 289 | .global memory_regions 290 | memory_regions: // start, incr, count 291 | .word 0x0004140E // 0x00000000 - 0x0FFFFFFF 292 | .word 0x01000000 // Outer and Inner Write-Back 293 | .word 16 // TEX 001, C 1, B 1 Alloc on Write 294 | 295 | .word 0x00000000 // 0x10000000 - 0xBFFFFFFF 296 | .word 0x00000000 // unmapped 297 | .word 176 298 | 299 | .word 0x0004140E // 0xC0000000 - 0xCFFFFFFF 300 | .word 0x01000000 // Outer and Inner Write-Back 301 | .word 16 // TEX 001, C 1, B 1 Alloc on Write 302 | 303 | .word 0x1004040A // 0xD0000000 - 0xDFFFFFFF 304 | .word 0x01000000 // Outer and Inner Write-Through 305 | .word 16 // TEX 000, C 1, B 0 No Alloc on Write 306 | 307 | .word 0x20040402 // 0xE0000000 - 0xFFFFFFFF 308 | .word 0x01000000 // peripherals (memory = device) 309 | .word 32 // TEX 000, C 0, B 0 not cached 310 | -------------------------------------------------------------------------------- /entry.S: -------------------------------------------------------------------------------- 1 | /* entry.S - exception, interrupt and syscall entry point */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | .section ".text" 20 | 21 | .balign 32 22 | .globl exception_table 23 | exception_table: 24 | ldr pc, addr_exception_reset 25 | ldr pc, addr_exception_undefined 26 | ldr pc, addr_exception_syscall 27 | ldr pc, addr_exception_prefetch_abort 28 | ldr pc, addr_exception_data_abort 29 | ldr pc, addr_exception_reserved 30 | ldr pc, addr_exception_irq 31 | ldr pc, addr_exception_fiq 32 | 33 | addr_exception_reset: .word exception_reset 34 | addr_exception_undefined: .word exception_undefined 35 | addr_exception_syscall: .word exception_syscall 36 | addr_exception_prefetch_abort: .word exception_prefetch_abort 37 | addr_exception_data_abort: .word exception_data_abort 38 | addr_exception_reserved: .word exception_reserved 39 | addr_exception_irq: .word exception_irq 40 | addr_exception_fiq: .word exception_fiq 41 | 42 | // Mapping between virtual and physical memory 43 | #define PHYS_TO_VIRT 0xC0000000 44 | 45 | .macro save, offset 46 | // Adjust LR and save it 47 | sub lr, #\offset 48 | stmdb sp!,{lr} 49 | // save all registers 50 | stmdb sp,{r0-r14}^ 51 | sub sp, #60 52 | mov r0, sp 53 | .endm 54 | 55 | .macro restore 56 | // restore all registers and return 57 | ldmia sp,{r0-r14}^ 58 | add sp, #60 59 | ldmia sp!,{pc}^ 60 | .endm 61 | 62 | .balign 4 63 | .globl exception_reset 64 | exception_reset: 65 | save 4 66 | bl exception_reset_handler 67 | // no way to return from reset 68 | // restore 69 | b abort 70 | 71 | .globl exception_undefined 72 | exception_undefined: 73 | save 4 74 | bl exception_undefined_handler 75 | b abort 76 | 77 | .globl exception_syscall 78 | exception_syscall: 79 | save 0 80 | bl exception_syscall_handler 81 | b abort 82 | 83 | .globl exception_prefetch_abort 84 | exception_prefetch_abort: 85 | save 4 86 | bl exception_prefetch_abort_handler 87 | b abort 88 | 89 | .globl exception_data_abort 90 | exception_data_abort: 91 | save 8 92 | bl exception_data_abort_handler 93 | b abort 94 | 95 | .globl exception_reserved 96 | exception_reserved: 97 | b abort 98 | 99 | .globl exception_irq 100 | exception_irq: 101 | save 4 102 | bl exception_irq_handler 103 | restore 104 | 105 | .globl exception_fiq 106 | exception_fiq: 107 | save 4 108 | bl exception_fiq_handler 109 | b abort 110 | -------------------------------------------------------------------------------- /foo.ml: -------------------------------------------------------------------------------- 1 | let rec fac = function 2 | | 1 -> 1 3 | | n -> n * fac (n-1) 4 | 5 | let () = ignore (fac 10) 6 | let () = Printf.printf "Hello World\n%!" 7 | let handler num = 8 | (* Printf.printf "Signal number %d\n%!" num; 9 | *) 10 | Thread.yield(); 11 | () 12 | 13 | let () = Sys.set_signal 0 (Sys.Signal_handle handler) 14 | 15 | let rec fib lst = function 16 | | 0 -> (1, "0", lst) 17 | | 1 -> (1, "1", lst) 18 | | n -> 19 | let s = Printf.sprintf "%d" n in 20 | let (n1, _, lst) = fib (s::lst) (n - 1) in 21 | let (n2, _, lst) = fib (s::lst) (n - 2) in 22 | (n1 + n2, s, lst) 23 | 24 | let rec loop n = 25 | let (res, s, _) = fib [] n 26 | in 27 | Printf.printf "[%s] fib(%s) = %d\n%!" (Time.to_string (Time.time ())) s res; 28 | (* 29 | Thread.yield(); 30 | Thread.signal 0; 31 | *) 32 | loop (n + 1) 33 | 34 | let rec loop2 n = 35 | let (res, s, _) = fib [] n 36 | in 37 | Printf.printf "[%s] fib2(%s) = %d\n%!" (Time.to_string (Time.time ())) s res; 38 | (* 39 | Thread.yield(); 40 | Thread.signal 0; 41 | *) 42 | loop2 (n + 1) 43 | 44 | let t = Thread.create (fun () -> loop2 1) 45 | let t = Thread.create (fun () -> loop 1) 46 | 47 | let rec loop3 n = 48 | Printf.printf "[%s] loop3 %d\n%!" (Time.to_string (Time.time ())) n; 49 | Thread.yield (); 50 | (* 51 | Gc.major (); 52 | Gc.compact (); 53 | *) let stat = Gc.stat () 54 | in 55 | Printf.printf "live_words = %d\n%!" stat.Gc.live_words; 56 | loop3 (n+1) 57 | 58 | let () = 59 | Gc.set { (Gc.get()) with Gc.verbose = 0x00d }; 60 | loop3 1 61 | -------------------------------------------------------------------------------- /link-arm-eabi.ld: -------------------------------------------------------------------------------- 1 | /* link-arm-eabi.ld - linker script for arm eabi */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | ENTRY(Start) 20 | 21 | VIRT_BASE = 0xC0000000; 22 | 23 | SECTIONS 24 | { 25 | /* Starts at LOADER_ADDR. */ 26 | /* . = 0xC0010000; */ 27 | . = 0xC0008000; 28 | _start = .; 29 | _text_start = .; 30 | .text : AT(ADDR(.text) - VIRT_BASE) { 31 | KEEP(*(.text.boot)) 32 | *(.text) 33 | *(.text.*) 34 | } 35 | . = ALIGN(4096); /* align to page size */ 36 | _text_end = .; 37 | /* .ARM.exidx is sorted, so has to go in its own output section. */ 38 | __exidx_start = .; 39 | .ARM.exidx : AT(ADDR(.ARM.exidx) - VIRT_BASE) { 40 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 41 | } 42 | __exidx_end = .; 43 | _rodata_start = .; 44 | .rodata : AT(ADDR(.rodata) - VIRT_BASE) { 45 | *(SORT_BY_ALIGNMENT(.rodata)) 46 | *(SORT_BY_ALIGNMENT(.rodata.*)) 47 | } 48 | .init_array ALIGN(4) : AT(ADDR(.init_array) - VIRT_BASE) { 49 | _init_array_start = .; 50 | *(.init_array) 51 | *(SORT_BY_INIT_PRIORITY(.init_array.*)) 52 | _init_array_end = .; 53 | } 54 | . = ALIGN(4096); /* align to page size */ 55 | _rodata_end = .; 56 | _data_start = .; 57 | .data : AT(ADDR(.data) - VIRT_BASE) { 58 | *(SORT_BY_ALIGNMENT(.data)) 59 | *(SORT_BY_ALIGNMENT(.data.*)) 60 | } 61 | .got : AT(ADDR(.got) - VIRT_BASE) { *(.got.plt) *(.got) } 62 | .dynamic : AT(ADDR(.dynamic) - VIRT_BASE) { *(.dynamic) } 63 | _data_end = .; 64 | .bss ALIGN(4) : AT(ADDR(.bss) - VIRT_BASE) { 65 | KEEP(*(.bss.prebss.*)) 66 | . = ALIGN(4); 67 | _bss_start = .; 68 | *(SORT_BY_ALIGNMENT(.bss)) 69 | *(SORT_BY_ALIGNMENT(.bss.*)) 70 | *(.gnu.linkonce.b.*) 71 | *(COMMON) 72 | . = ALIGN(4096); /* align to page size */ 73 | _bss_end = .; 74 | _stack_start = .; 75 | . = _stack_start + 1024*1024; 76 | _stack_end = .; 77 | _fiq_stack_start = .; 78 | . = _fiq_stack_start + 16384; 79 | _fiq_stack_end = .; 80 | _irq_stack_start = .; 81 | . = _irq_stack_start + 16384; 82 | _irq_stack_end = .; 83 | _svc_stack_start = .; 84 | . = _svc_stack_start + 16384; 85 | _svc_stack_end = .; 86 | _abt_stack_start = .; 87 | . = _abt_stack_start + 16384; 88 | _abt_stack_end = .; 89 | _und_stack_start = .; 90 | . = _und_stack_start + 16384; 91 | _und_stack_end = .; 92 | } 93 | 94 | _end = .; 95 | /* Strip unnecessary stuff */ 96 | /DISCARD/ : { *(*) } 97 | } 98 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* list.h - Abstract list handling 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Data structures and helper functions for doubly linked cyclic lists. 20 | */ 21 | 22 | #ifndef OCAML_RPI__LIST_H 23 | #define OCAML_RPI__LIST_H 24 | 25 | #include 26 | 27 | typedef struct DList DList; 28 | struct DList { 29 | DList *next; 30 | DList *prev; 31 | }; 32 | 33 | // initialize a one element DList 34 | static inline void dlist_init(DList *dlist) { 35 | // printf("%s(%p)\n", __FUNCTION__, dlist); 36 | dlist->next = dlist; 37 | dlist->prev = dlist; 38 | } 39 | 40 | // insert d2 after d1 41 | static inline void dlist_insert_after(DList *d1, DList *d2) { 42 | // printf("%s(%p, %p)\n", __FUNCTION__, d1, d2); 43 | DList *n1 = d1->next; 44 | DList *e2 = d2->prev; 45 | 46 | d1->next = d2; 47 | d2->prev = d1; 48 | e2->next = n1; 49 | n1->prev = e2; 50 | } 51 | 52 | // insert d2 before d1 53 | static inline void dlist_insert_before(DList *d1, DList *d2) { 54 | // printf("%s(%p, %p)\n", __FUNCTION__, d1, d2); 55 | DList *e1 = d1->prev; 56 | DList *e2 = d2->prev; 57 | 58 | e1->next = d2; 59 | d2->prev = e1; 60 | e2->next = d1; 61 | d1->prev = e2; 62 | } 63 | 64 | // remove d from the list 65 | static inline void dlist_remove(DList *d) { 66 | // printf("%s(%p)\n", __FUNCTION__, d); 67 | d->prev->next = d->next; 68 | d->next->prev = d->prev; 69 | d->next = d; 70 | d->prev = d; 71 | } 72 | 73 | // push d2 to the front of the d1p list 74 | static inline void dlist_push(DList **d1p, DList *d2) { 75 | // printf("%s(%p, %p)\n", __FUNCTION__, d1p, d2); 76 | if (*d1p != NULL) { 77 | dlist_insert_before(*d1p, d2); 78 | } 79 | *d1p = d2; 80 | } 81 | 82 | // pop the front of the dp list 83 | static inline DList * dlist_pop(DList **dp) { 84 | // printf("%s(%p)\n", __FUNCTION__, dp); 85 | DList *d1 = *dp; 86 | DList *d2 = d1->next; 87 | dlist_remove(d1); 88 | if (d1 == d2) { 89 | *dp = NULL; 90 | } else { 91 | *dp = d2; 92 | } 93 | return d1; 94 | } 95 | 96 | // remove d2 from the list, advancing d1p if needed 97 | static inline void dlist_remove_from(DList **d1p, DList *d2) { 98 | // printf("%s(%p, %p)\n", __FUNCTION__, d1p, d2); 99 | if (*d1p == d2) { 100 | dlist_pop(d1p); 101 | } else { 102 | dlist_remove(d2); 103 | } 104 | } 105 | 106 | #define CONTAINER(C, l, v) ((C*)(((intptr_t)v) - (intptr_t)&(((C*)0)->l))) 107 | #define OFFSETOF(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 108 | 109 | #define DLIST_INIT(v, l) dlist_init(&v->l) 110 | 111 | #define DLIST_REMOVE_FROM(h, d, l) \ 112 | { \ 113 | typeof(**h) **h_ = h, *d_ = d; \ 114 | DList *head = &(*h_)->l; \ 115 | dlist_remove_from(&head, &d_->l); \ 116 | if (head == NULL) { \ 117 | *h_ = NULL; \ 118 | } else { \ 119 | *h_ = CONTAINER(typeof(**h), l, head); \ 120 | } \ 121 | } 122 | 123 | #define DLIST_PUSH(h, v, l) \ 124 | { \ 125 | typeof(*v) **h_ = h, *v_ = v; \ 126 | DList *head = &(*h_)->l; \ 127 | if (*h_ == NULL) head = NULL; \ 128 | dlist_push(&head, &v_->l); \ 129 | *h_ = CONTAINER(typeof(*v), l, head); \ 130 | } 131 | 132 | #define DLIST_POP(h, l) \ 133 | ({ \ 134 | typeof(**h) **h_ = h; \ 135 | DList *head = &(*h_)->l; \ 136 | DList *res = dlist_pop(&head); \ 137 | if (head == NULL) { \ 138 | *h_ = NULL; \ 139 | } else { \ 140 | *h_ = CONTAINER(typeof(**h), l, head); \ 141 | } \ 142 | CONTAINER(typeof(**h), l, res); \ 143 | }) 144 | 145 | #define DLIST_ITERATOR_BEGIN(h, l, it) \ 146 | { \ 147 | typeof(*h) *h_ = h; \ 148 | DList *last_##it = h_->l.prev, *iter_##it = &h_->l, *next_##it; \ 149 | do { \ 150 | if (iter_##it == last_##it) { \ 151 | next_##it = NULL; \ 152 | } else { \ 153 | next_##it = iter_##it->next; \ 154 | } \ 155 | typeof(*h)* it = CONTAINER(typeof(*h), l, iter_##it); 156 | 157 | #define DLIST_ITERATOR_END(it) \ 158 | } while((iter_##it = next_##it)); \ 159 | } 160 | 161 | #endif // #ifndef OCAML_RPI__LIST_H 162 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "uart.h" 6 | #include "printf.h" 7 | #include "string.h" 8 | #include "memory.h" 9 | 10 | #define UNUSED(x) (void)(x) 11 | 12 | // error 13 | int errno; 14 | #define EINVAL 22 15 | #define ERANGE 34 16 | 17 | // limit 18 | #define LONG_MAX 0x7FFFFFFFL 19 | #define LONG_MIN (-LONG_MAX-1) 20 | 21 | // ocaml entry function 22 | extern int caml_startup(char **argv); 23 | 24 | // constructors 25 | typedef void (*constructor_t)(void); 26 | extern constructor_t _init_array_start[]; 27 | extern constructor_t _init_array_end[]; 28 | void kernel_constructors(void) { 29 | for(constructor_t *fn = _init_array_start; fn != _init_array_end; ++fn) { 30 | (*fn)(); 31 | } 32 | } 33 | 34 | // memory 35 | //extern char *_end; 36 | //void *end = (void*)(((char*)&_end) + 4); 37 | extern void *_end[]; 38 | 39 | // entry.S 40 | extern uint32_t exception_table[]; 41 | 42 | double test_double(void) { 43 | volatile double x = 2.0; 44 | volatile double y = 3.0; 45 | double z = x * y; 46 | asm volatile("vmov %d0, %r0, %r1"); 47 | return z; 48 | } 49 | 50 | // enable interrupts 51 | static inline void enable_irq(void) { 52 | uint32_t t; 53 | asm volatile("mrs %[t],cpsr; bic %[t], %[t], #0x80; msr cpsr_c, %[t]" 54 | : [t]"=r"(t)); 55 | } 56 | 57 | // disable interrupts 58 | static inline void disable_irq(void) { 59 | uint32_t t; 60 | asm volatile("mrs %[t],cpsr; orr %[t], %[t], #0x80; msr cpsr_c, %[t]" 61 | : [t]"=r"(t)); 62 | } 63 | 64 | long int strtol(const char *nptr, char **endptr, int base); 65 | void test_strtol(void) { 66 | errno = 0; printf("strtol(\"foo\", NULL, 0) = %ld, errno = %d\n", strtol("foo", NULL, 0), errno); 67 | errno = 0; printf("strtol(\"0\", NULL, -1) = %ld, errno = %d\n", strtol("0", NULL, -1), errno); 68 | errno = 0; printf("strtol(\"0\", NULL, 1) = %ld, errno = %d\n", strtol("0", NULL, 1), errno); 69 | errno = 0; printf("strtol(\"0\", NULL, 37) = %ld, errno = %d\n", strtol("0", NULL, 37), errno); 70 | errno = 0; printf("strtol(\"0\", NULL, 0) = %ld, errno = %d\n", strtol("0", NULL, 0), errno); 71 | errno = 0; printf("strtol(\"0\", NULL, 8) = %ld, errno = %d\n", strtol("0", NULL, 8), errno); 72 | errno = 0; printf("strtol(\"0\", NULL, 16) = %ld, errno = %d\n", strtol("0", NULL, 16), errno); 73 | errno = 0; printf("strtol(\"123\", NULL, 10) = %ld, errno = %d\n", strtol("123", NULL, 10), errno); 74 | errno = 0; printf("strtol(\"20\", NULL, 16) = %ld, errno = %d\n", strtol("20", NULL, 16), errno); 75 | errno = 0; printf("strtol(\"78\", NULL, 8) = %ld, errno = %d\n", strtol("78", NULL, 8), errno); 76 | errno = 0; printf("strtol(\"08\", NULL, 0) = %ld, errno = %d\n", strtol("08", NULL, 0), errno); 77 | errno = 0; printf("strtol(\"0x20\", NULL, 0) = %ld, errno = %d\n", strtol("0x20", NULL, 0), errno); 78 | errno = 0; printf("strtol(\"017\", NULL, 0) = %ld, errno = %d\n", strtol("017", NULL, 0), errno); 79 | errno = 0; printf("strtol(\"0x\", NULL, 0) = %ld, errno = %d\n", strtol("0x", NULL, 0), errno); 80 | errno = 0; printf("strtol(\"+0x20\", NULL, 0) = %ld, errno = %d\n", strtol("+0x20", NULL, 0), errno); 81 | errno = 0; printf("strtol(\"-0x20\", NULL, 0) = %ld, errno = %d\n", strtol("-0x20", NULL, 0), errno); 82 | errno = 0; printf("strtol(\"0x81111111\", NULL, 16) = %ld, errno = %d\n", strtol("0x81111111", NULL, 16), errno); 83 | errno = 0; printf("strtol(\"-0x81111111\", NULL, 16) = %ld, errno = %d\n", strtol("-0x81111111", NULL, 16), errno); 84 | } 85 | 86 | void kernel_main(int zero, int model, void *atags) { 87 | (void)zero; 88 | (void)model; 89 | (void)atags; 90 | char arg0[] = "ocaml kernel"; 91 | char *argv[] = {arg0, NULL}; 92 | uart_init(); 93 | puts("\n# uart initialized\n"); 94 | // delay(100000000); 95 | 96 | printf("_end = %p\n", _end); 97 | printf("model = %#x [expected 0xc42]\n", model); 98 | printf("atags @ %p\n", atags); 99 | uint32_t mem_size = ((uint32_t*)atags)[7]; 100 | printf("memory size = %#x\n", mem_size); 101 | memory_init(_end, mem_size - ((intptr_t)_end - 0xC0000000)); 102 | { 103 | char c; 104 | printf("# stack = %p\n", &c); 105 | } 106 | 107 | // set exception vector base address register 108 | asm volatile("mcr p15, 0, %[addr], c12, c0, 0" 109 | : : [addr]"r"(exception_table)); 110 | puts("# exception vector set\n"); 111 | // delay(100000000); 112 | 113 | puts("# enabling IRQs\n"); 114 | enable_irq(); 115 | 116 | printf("%06d\n", 0); 117 | 118 | test_double(); 119 | puts("# doubles tested\n"); 120 | // delay(100000000); 121 | 122 | test_strtol(); 123 | 124 | //delay(100000000); 125 | caml_startup(argv); 126 | // delay(100000000); 127 | panic("all done\n"); 128 | } 129 | 130 | /*************************************************************************** 131 | * math functions * 132 | ***************************************************************************/ 133 | double acos(double x) { 134 | puts("# "); puts(__FUNCTION__); puts("()\n"); 135 | UNUSED(x); 136 | return 0; 137 | } 138 | double asin(double x) { 139 | puts("# "); puts(__FUNCTION__); puts("()\n"); 140 | UNUSED(x); 141 | return 0; 142 | } 143 | double atan(double x) { 144 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 145 | UNUSED(x); 146 | return 0; 147 | } 148 | double atan2(double y, double x) { 149 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 150 | UNUSED(x); 151 | UNUSED(y); 152 | return 0; 153 | } 154 | double ceil(double x) { 155 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 156 | UNUSED(x); 157 | return 0; 158 | } 159 | double cos(double x) { 160 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 161 | UNUSED(x); 162 | return 0; 163 | } 164 | double cosh(double x) { 165 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 166 | UNUSED(x); 167 | return 0; 168 | } 169 | double exp(double x) { 170 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 171 | UNUSED(x); 172 | return 0; 173 | } 174 | double expm1(double x) { 175 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 176 | UNUSED(x); 177 | return 0; 178 | } 179 | double floor(double x) { 180 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 181 | UNUSED(x); 182 | return 0; 183 | } 184 | double fmod(double x, double y) { 185 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 186 | UNUSED(x); 187 | UNUSED(y); 188 | return 0; 189 | } 190 | double frexp(double x, int *expo) { 191 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 192 | UNUSED(x); 193 | UNUSED(expo); 194 | return 0; 195 | } 196 | double hypot(double x, double y) { 197 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 198 | UNUSED(x); 199 | UNUSED(y); 200 | return 0; 201 | } 202 | double ldexp(double x, int expo) { 203 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 204 | UNUSED(x); 205 | UNUSED(expo); 206 | return 0; 207 | } 208 | double log(double x) { 209 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 210 | UNUSED(x); 211 | return 0; 212 | } 213 | double log10(double x) { 214 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 215 | UNUSED(x); 216 | return 0; 217 | } 218 | double log1p(double x) { 219 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 220 | UNUSED(x); 221 | return 0; 222 | } 223 | double modf(double x, double *iptr) { 224 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 225 | UNUSED(x); 226 | UNUSED(iptr); 227 | return 0; 228 | } 229 | double pow(double x, double y) { 230 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 231 | UNUSED(x); 232 | UNUSED(y); 233 | return 0; 234 | } 235 | double sin(double x) { 236 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 237 | UNUSED(x); 238 | return 0; 239 | } 240 | double sinh(double x) { 241 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 242 | UNUSED(x); 243 | return 0; 244 | } 245 | double sqrt(double x) { 246 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 247 | UNUSED(x); 248 | return 0; 249 | } 250 | double tan(double x) { 251 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 252 | UNUSED(x); 253 | return 0; 254 | } 255 | double tanh(double x) { 256 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 257 | UNUSED(x); 258 | return 0; 259 | } 260 | int __fpclassify(double x) { 261 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 262 | UNUSED(x); 263 | return 0; 264 | } 265 | 266 | /*************************************************************************** 267 | * C functions * 268 | ***************************************************************************/ 269 | 270 | // IO 271 | typedef struct FILE { 272 | int fd; 273 | char pad[66536]; 274 | } FILE; 275 | 276 | FILE stdin = { .fd = 0 }; 277 | FILE stdout = { .fd = 1 }; 278 | FILE stderr = { .fd = 2 }; 279 | 280 | int fflush(FILE *stream) { 281 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 282 | UNUSED(stream); 283 | return 0; 284 | } 285 | 286 | int fputc(int c, FILE *stream) { 287 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 288 | UNUSED(stream); 289 | return putchar(c); 290 | } 291 | 292 | int fputs(const char *s, FILE *stream) { 293 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 294 | UNUSED(stream); 295 | return puts(s); 296 | } 297 | 298 | ssize_t write(int fd, const void *buf, size_t count) { 299 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 300 | UNUSED(fd); 301 | size_t n = count; 302 | const char *p = (const char *)buf; 303 | while(n-- > 0) putchar(*p++); 304 | return count; 305 | } 306 | 307 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { 308 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 309 | UNUSED(stream); 310 | size *= nmemb; 311 | size_t n = size; 312 | const char *p = (const char *)ptr; 313 | while(n-- > 0) putchar(*p++); 314 | return size; 315 | } 316 | 317 | ssize_t read(int fd, void *buf, size_t count) { 318 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 319 | UNUSED(fd); 320 | UNUSED(buf); 321 | UNUSED(count); 322 | // FIXME: read from uart 323 | panic("MISSING: read()"); 324 | return -1; 325 | } 326 | 327 | // exit 328 | void __attribute__((noreturn)) exit(int status) { 329 | UNUSED(status); 330 | panic("exit()"); 331 | } 332 | 333 | // filesystem 334 | int chdir(const char *path) { 335 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 336 | UNUSED(path); 337 | return -1; 338 | } 339 | 340 | int close(int fd) { 341 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 342 | UNUSED(fd); 343 | return 0; 344 | } 345 | 346 | typedef struct DIR { } DIR; 347 | 348 | int closedir(DIR *dirp) { 349 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 350 | UNUSED(dirp); 351 | return 0; 352 | } 353 | 354 | int fcntl(int fd, int cmd, ... /* arg */ ) { 355 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 356 | UNUSED(fd); 357 | UNUSED(cmd); 358 | return -1; 359 | } 360 | 361 | char *getcwd(char *buf, size_t size) { 362 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 363 | UNUSED(size); 364 | buf[0] = '/'; 365 | buf[1] = 0; 366 | return buf; 367 | } 368 | 369 | //typedef uint64_t off_t; 370 | 371 | off_t lseek64(int fd, off_t offset, int whence) { 372 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 373 | UNUSED(fd); 374 | UNUSED(offset); 375 | UNUSED(whence); 376 | return (off_t)-1; 377 | } 378 | 379 | // typedef int mode_t; 380 | int open64(const char *pathname, int flags, mode_t mode) { 381 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 382 | UNUSED(pathname); 383 | UNUSED(flags); 384 | UNUSED(mode); 385 | return -1; 386 | } 387 | 388 | DIR *opendir(const char *name) { 389 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 390 | UNUSED(name); 391 | return NULL; 392 | } 393 | 394 | struct dirent; 395 | 396 | struct dirent *readdir64(DIR *dirp) { 397 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 398 | UNUSED(dirp); 399 | return NULL; 400 | } 401 | 402 | ssize_t readlink(const char *path, char *buf, size_t bufsiz) { 403 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 404 | UNUSED(path); 405 | UNUSED(buf); 406 | UNUSED(bufsiz); 407 | return -1; 408 | } 409 | 410 | int rename(const char *oldpath, const char *newpath) { 411 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 412 | UNUSED(oldpath); 413 | UNUSED(newpath); 414 | return -1; 415 | } 416 | 417 | int unlink(const char *pathname) { 418 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 419 | UNUSED(pathname); 420 | return -1; 421 | } 422 | 423 | struct stat; 424 | 425 | int __xstat64(const char *path, struct stat *buf) { 426 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 427 | UNUSED(path); 428 | UNUSED(buf); 429 | return -1; 430 | } 431 | 432 | // processes 433 | char *getenv(const char *name) { 434 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 435 | UNUSED(name); 436 | return NULL; 437 | } 438 | 439 | typedef int pid_t; 440 | 441 | pid_t getpid(void) { 442 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 443 | return 1; 444 | } 445 | 446 | pid_t getppid(void) { 447 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 448 | return 0; 449 | } 450 | 451 | struct rlimit; 452 | 453 | int getrlimit(int resource, struct rlimit *rlim) { 454 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 455 | UNUSED(resource); 456 | UNUSED(rlim); 457 | return -1; 458 | } 459 | 460 | struct rusage; 461 | 462 | int getrusage(int who, struct rusage *usage) { 463 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 464 | UNUSED(who); 465 | UNUSED(usage); 466 | return -1; 467 | } 468 | 469 | // time 470 | struct timeval; 471 | struct timezone; 472 | 473 | int gettimeofday(struct timeval *tv, struct timezone *tz) { 474 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 475 | UNUSED(tv); 476 | UNUSED(tz); 477 | return -1; 478 | } 479 | 480 | // locale 481 | char *setlocale(int category, const char *locale) { 482 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 483 | UNUSED(category); 484 | UNUSED(locale); 485 | return NULL; 486 | } 487 | 488 | const unsigned short **__ctype_b_loc (void) { 489 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 490 | static const unsigned short *ctypes[384]; 491 | return &ctypes[128]; 492 | } 493 | 494 | // signals 495 | //struct sigaction; 496 | 497 | int sigaction(int signum, const struct sigaction *act, 498 | struct sigaction *oldact) { 499 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 500 | UNUSED(signum); 501 | UNUSED(act); 502 | UNUSED(oldact); 503 | if (oldact) { 504 | memset(oldact, 0, sizeof(struct sigaction)); 505 | oldact->sa_handler = SIG_DFL; 506 | } 507 | return 0; 508 | } 509 | 510 | // typedef struct { } sigset_t; 511 | 512 | int sigaddset(sigset_t *set, int signum) { 513 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 514 | UNUSED(set); 515 | UNUSED(signum); 516 | return -1; 517 | } 518 | 519 | int sigaltstack (const struct sigaltstack *__restrict ss, 520 | struct sigaltstack *__restrict oss) { 521 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 522 | UNUSED(ss); 523 | UNUSED(oss); 524 | return -1; 525 | } 526 | 527 | int sigdelset(sigset_t *set, int signum) { 528 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 529 | UNUSED(set); 530 | UNUSED(signum); 531 | return -1; 532 | } 533 | 534 | int sigemptyset(sigset_t *set) { 535 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 536 | UNUSED(set); 537 | return -1; 538 | } 539 | 540 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { 541 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 542 | UNUSED(how); 543 | UNUSED(set); 544 | UNUSED(oldset); 545 | return -1; 546 | } 547 | 548 | double strtod(const char *nptr, char **endptr) { 549 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 550 | UNUSED(nptr); 551 | UNUSED(endptr); 552 | return 0.0; 553 | } 554 | 555 | int isspace(int c) { 556 | switch(c) { 557 | case '\f': 558 | case '\n': 559 | case '\r': 560 | case '\t': 561 | case '\v': 562 | case ' ': 563 | return 1; 564 | break; 565 | default: 566 | return 0; 567 | } 568 | } 569 | 570 | int isdigit(int c) { 571 | if (c >= '0' && c <= '9') return 1; 572 | return 0; 573 | } 574 | 575 | int digit(int c) { 576 | if (c >= '0' && c <= '9') return c - '0'; 577 | if (c >= 'A' && c <= 'Z') return c - 'A' + 16; 578 | if (c >= 'a' && c <= 'z') return c - 'a' + 16; 579 | return 256; 580 | } 581 | 582 | long int strtol(const char *nptr, char **endptr, int base) { 583 | long int res = 0; 584 | int sign = 1; 585 | const char *ptr = nptr; 586 | const char *p = nptr; 587 | // printf("# %s(%s, %p, %d)\n", __FUNCTION__, nptr, endptr, base); 588 | // sanity check 589 | if (!nptr || base == 1 || base < 0 || base > 36) { 590 | errno = EINVAL; 591 | goto out; 592 | } 593 | 594 | // skip leading whitespace 595 | while(*p != '\0' && isspace(*p)) ++p; 596 | if (*p == '\0') goto out; 597 | 598 | // parse sign 599 | if (*p == '+') { 600 | ++p; 601 | } 602 | if (*p == '-') { 603 | ++p; 604 | sign = -1; 605 | } 606 | if (*p == '\0') goto out; 607 | 608 | // detect base for hex 609 | if (base == 0 || base == 16) { 610 | if (p[0] == '0' && p[1] == 'x' && digit(p[2]) < 16) { 611 | ptr = p + 2; 612 | base = 16; 613 | } 614 | } 615 | 616 | // Handle base detection for octal 617 | if (base == 0 && p[0] == '0') { 618 | ptr = p + 1; 619 | base = 8; 620 | } 621 | 622 | // Fall back on decimal when unknown 623 | if (base == 0) { 624 | base = 10; 625 | } 626 | 627 | // parse base prefix 628 | const char *q = p; 629 | switch(base) { 630 | case 0: 631 | if (*q == '0') { 632 | ++q; 633 | if (*q == 'x' || *q == 'X') { 634 | ++q; 635 | if (digit(*q) < 16) { 636 | base = 16; 637 | ptr = q; 638 | } else { 639 | base = 10; 640 | ptr = p; 641 | } 642 | } else { 643 | base = 8; 644 | ptr = q; 645 | } 646 | } else if (digit(*q) < 10) { 647 | base = 10; 648 | ptr = p; 649 | } else { 650 | base = 10; 651 | } 652 | break; 653 | case 16: 654 | if (*q == '0') { 655 | ++q; 656 | if (*q == 'x' || *q == 'X') { 657 | ++q; 658 | if (digit(*q) < 16) { 659 | ptr = q; 660 | } else { 661 | ptr = p; 662 | } 663 | } else { 664 | ptr = p; 665 | } 666 | } else if (digit(*q) <= 16) { 667 | ptr = p; 668 | } 669 | break; 670 | default: 671 | if (digit(*q) < base) { 672 | ptr = q; 673 | } 674 | } 675 | // parse number 676 | long int min = LONG_MIN / base; 677 | int d; 678 | while((d = digit(*ptr)) < base) { 679 | if (res < min) { 680 | res = LONG_MIN; 681 | errno = ERANGE; 682 | } else { 683 | res *= base; 684 | if (res < LONG_MIN + d) { 685 | res = LONG_MIN; 686 | errno = ERANGE; 687 | } else { 688 | res -= d; 689 | } 690 | } 691 | ++ptr; 692 | } 693 | 694 | out: 695 | // printf("nptr = '%s', ptr = '%s', res = %ld\n", nptr, ptr, -res); 696 | if (endptr) *endptr = (char*)ptr; 697 | if (nptr == ptr) errno = EINVAL; 698 | if (sign == 1) { 699 | if (res == LONG_MIN) { 700 | errno = ERANGE; 701 | return LONG_MAX; 702 | } else { 703 | return -res; 704 | } 705 | } else { 706 | return res; 707 | } 708 | } 709 | 710 | int * __errno_location(void) { 711 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 712 | return &errno; 713 | } 714 | 715 | char *strerror(int errnum) { 716 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 717 | UNUSED(errnum); 718 | return NULL; 719 | } 720 | 721 | // system 722 | int system(const char *command) { 723 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 724 | UNUSED(command); 725 | return -1; 726 | } 727 | 728 | // stack protector 729 | void * __stack_chk_guard = NULL; 730 | 731 | void __stack_chk_guard_setup(void) { 732 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 733 | int * p; 734 | p = (int *) &__stack_chk_guard; 735 | 736 | /* If you have the ability to generate random numbers in your kernel then use them, 737 | otherwise for 32-bit code: */ 738 | *p = 0x00000aff; 739 | } 740 | 741 | void __attribute__((noreturn)) __stack_chk_fail(void) { 742 | panic("__stack_chk_fail()"); 743 | } 744 | 745 | // printf / scanf 746 | 747 | int __sprintf_chk(char * str, int flag, size_t len, const char * format, ...) { 748 | puts("# "); puts(__FUNCTION__); puts("()\n"); //delay(100000000); 749 | UNUSED(flag); 750 | va_list args; 751 | 752 | va_start(args, format); 753 | len = vsnprintf(str, len, format, args); 754 | va_end(args); 755 | 756 | return len; 757 | } 758 | 759 | #define BUF_SIZE 4096 760 | int sprintf(char * str, const char * format, ...) { 761 | // puts("# "); puts(__FUNCTION__); puts("()\n"); //delay(100000000); 762 | va_list args; 763 | size_t len; 764 | 765 | va_start(args, format); 766 | len = vsnprintf(str, BUF_SIZE, format, args); 767 | va_end(args); 768 | 769 | return len; 770 | } 771 | 772 | int __fprintf_chk(FILE * stream, int flag, const char * format, ...) { 773 | puts("# "); puts(__FUNCTION__); puts("()\n"); //delay(100000000); 774 | UNUSED(stream); 775 | UNUSED(flag); 776 | va_list args; 777 | char buf[BUF_SIZE]; 778 | ssize_t len; 779 | va_start(args, format); 780 | len = vsnprintf(buf, BUF_SIZE, format, args); 781 | va_end(args); 782 | if (len != -1 && len <= BUF_SIZE) { 783 | puts(buf); 784 | } else { 785 | // FIXME: too long 786 | panic("printf: too long\n"); 787 | } 788 | return len; 789 | } 790 | 791 | int fprintf(FILE * stream, const char * format, ...) { 792 | puts("# "); puts(__FUNCTION__); puts("()\n"); //delay(100000000); 793 | UNUSED(stream); 794 | va_list args; 795 | char buf[BUF_SIZE]; 796 | ssize_t len; 797 | va_start(args, format); 798 | len = vsnprintf(buf, BUF_SIZE, format, args); 799 | va_end(args); 800 | if (len != -1 && len <= BUF_SIZE) { 801 | puts(buf); 802 | } else { 803 | // FIXME: too long 804 | panic("printf: too long\n"); 805 | } 806 | return len; 807 | } 808 | 809 | int __isoc99_sscanf(const char *format, ...) { 810 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 811 | UNUSED(format); 812 | return -1; 813 | } 814 | 815 | // sigsetjmp 816 | typedef struct { } sigjmp_buf; 817 | 818 | int __sigsetjmp(sigjmp_buf env, int savesigs) { 819 | puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 820 | UNUSED(env); 821 | UNUSED(savesigs); 822 | return 0; 823 | } 824 | 825 | // unwind 826 | int raise (int sig) { 827 | UNUSED(sig); 828 | panic("raise()\n"); 829 | } 830 | 831 | void __aeabi_unwind_cpp_pr0(void) { 832 | panic("__aeabi_unwind_cpp_pr0()\n"); 833 | } 834 | 835 | /*************************************************************************** 836 | * dl functions * 837 | ***************************************************************************/ 838 | void *dlopen(const char *filename, int flag) { 839 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 840 | UNUSED(filename); 841 | UNUSED(flag); 842 | return 0; 843 | } 844 | 845 | int dlclose(void *handle) { 846 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 847 | UNUSED(handle); 848 | return -1; 849 | } 850 | 851 | const char *dlerror(void) { 852 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 853 | return "ERROR: dlerror()\n"; 854 | } 855 | 856 | void *dlsym(void *handle, const char *symbol) { 857 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 858 | UNUSED(handle); UNUSED(symbol); 859 | return 0; 860 | } 861 | 862 | /*************************************************************************** 863 | * exception handlers * 864 | ***************************************************************************/ 865 | void dump(uint32_t *regs) { 866 | static const char *name[] = {"R0 ", "R1 ", "R2 ", "R3 ", 867 | "R4 ", "R5 ", "R6 ", "R7 ", 868 | "R8 ", "R9 ", "R10", "R11", 869 | "R12", "SPu", "LRu", "IPu"}; 870 | printf("regs @ %#8.8x", (uint32_t)regs); 871 | for(int i = 0; i < 16; ++i) { 872 | if (i % 4 == 0) putchar('\n'); 873 | printf("%s = %#8.8x ", name[i], regs[i]); 874 | } 875 | putchar('\n'); 876 | } 877 | 878 | void exception_reset_handler(uint32_t *regs) { 879 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 880 | dump(regs); 881 | } 882 | 883 | void exception_undefined_handler(uint32_t *regs) { 884 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 885 | dump(regs); 886 | } 887 | 888 | void exception_syscall_handler(uint32_t *regs) { 889 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 890 | dump(regs); 891 | } 892 | 893 | void exception_prefetch_abort_handler(uint32_t *regs) { 894 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 895 | dump(regs); 896 | } 897 | 898 | void exception_data_abort_handler(uint32_t *regs) { 899 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 900 | dump(regs); 901 | } 902 | 903 | extern void time_irq_timer1(uint32_t *regs); 904 | void exception_irq_handler(uint32_t *regs) { 905 | UNUSED(regs); 906 | puts("# "); puts(__FUNCTION__); puts("()\n"); 907 | // dump(regs); 908 | time_irq_timer1(regs); 909 | } 910 | 911 | void exception_fiq_handler(uint32_t *regs) { 912 | puts("# "); puts(__FUNCTION__); puts("()\n"); delay(100000000); 913 | dump(regs); 914 | } 915 | -------------------------------------------------------------------------------- /memory.c: -------------------------------------------------------------------------------- 1 | /* memory.c - Memory management 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Manage chunks of memory implementing malloc and free. 20 | */ 21 | 22 | // #include 23 | #include 24 | #include "list.h" 25 | #include "printf.h" 26 | #include "string.h" 27 | 28 | void delay(uint32_t); 29 | 30 | typedef struct Chunk Chunk; 31 | struct Chunk { 32 | DList all; 33 | int used; 34 | union { 35 | char data[0]; 36 | DList free; 37 | }; 38 | }; 39 | 40 | enum { 41 | NUM_SIZES = 32, 42 | ALIGN = __alignof__(Chunk), 43 | MIN_SIZE = sizeof(DList), 44 | HEADER_SIZE = OFFSETOF(Chunk, data), 45 | }; 46 | 47 | Chunk *free_chunk[NUM_SIZES] = { NULL }; 48 | size_t mem_free = 0; 49 | size_t mem_used = 0; 50 | size_t mem_meta = 0; 51 | Chunk *first = NULL; 52 | Chunk *last = NULL; 53 | 54 | void memory_chunk_init(Chunk *chunk) { 55 | // printf("%s(%p)\n", __FUNCTION__, chunk); 56 | DLIST_INIT(chunk, all); 57 | chunk->used = 0; 58 | DLIST_INIT(chunk, free); 59 | } 60 | 61 | size_t memory_chunk_size(const Chunk *chunk) { 62 | // printf("%s(%p)\n", __FUNCTION__, chunk); 63 | char *end = (char*)(chunk->all.next); 64 | char *start = (char*)(&chunk->all); 65 | return (end - start) - HEADER_SIZE; 66 | } 67 | 68 | int memory_chunk_slot(size_t size) { 69 | int n = -1; 70 | while(size > 0) { 71 | ++n; 72 | size /= 2; 73 | } 74 | return n; 75 | } 76 | 77 | void memory_init(void *mem, size_t size) { 78 | first = (Chunk*)(((intptr_t)mem + ALIGN - 1) & (~(ALIGN - 1))); 79 | last = ((Chunk*)(((intptr_t)mem + size) & (~(ALIGN - 1)))) - 1; 80 | Chunk *second = first + 1; 81 | memory_chunk_init(first); 82 | memory_chunk_init(second); 83 | memory_chunk_init(last); 84 | dlist_insert_after(&first->all, &second->all); 85 | dlist_insert_after(&second->all, &last->all); 86 | // mark first/last as used so they never get merged 87 | first->used = 1; 88 | last->used = 1; 89 | 90 | size_t len = memory_chunk_size(second); 91 | int n = memory_chunk_slot(len); 92 | printf("%s(%p, %#zx) : adding chunk %#zx [%d]\n", __FUNCTION__, mem, size, len, n); 93 | DLIST_PUSH(&free_chunk[n], second, free); 94 | mem_free = len - HEADER_SIZE; 95 | mem_meta = sizeof(Chunk) * 2 + HEADER_SIZE; 96 | } 97 | 98 | void *malloc(size_t size) { 99 | printf("%s(%#zx)\n", __FUNCTION__, size); 100 | size = (size + ALIGN - 1) & (~(ALIGN - 1)); 101 | if (size < MIN_SIZE) size = MIN_SIZE; 102 | int n = memory_chunk_slot(size - 1) + 1; 103 | if (n >= NUM_SIZES) return NULL; 104 | while(!free_chunk[n]) { 105 | ++n; 106 | if (n >= NUM_SIZES) return NULL; 107 | } 108 | Chunk *chunk = DLIST_POP(&free_chunk[n], free); 109 | size_t size2 = memory_chunk_size(chunk); 110 | // printf("@ %p [%#zx]\n", chunk, size2); 111 | size_t len = 0; 112 | if (size + sizeof(Chunk) <= size2) { 113 | Chunk *chunk2 = (Chunk*)((intptr_t)chunk + HEADER_SIZE + size); 114 | memory_chunk_init(chunk2); 115 | dlist_insert_after(&chunk->all, &chunk2->all); 116 | len = memory_chunk_size(chunk2); 117 | n = memory_chunk_slot(len); 118 | // printf(" adding chunk @ %p %#zx [%d]\n", chunk2, len, n); 119 | DLIST_PUSH(&free_chunk[n], chunk2, free); 120 | mem_meta += HEADER_SIZE; 121 | mem_free += len - HEADER_SIZE; 122 | } 123 | chunk->used = 1; 124 | mem_free -= size2; 125 | mem_used += size2 - len - HEADER_SIZE; 126 | printf(" = %p [%p]\n", chunk->data, chunk); 127 | return chunk->data; 128 | } 129 | 130 | void remove_free(Chunk *chunk) { 131 | size_t len = memory_chunk_size(chunk); 132 | int n = memory_chunk_slot(len); 133 | // printf("%s(%p) : removing chunk %#zx [%d]\n", __FUNCTION__, chunk, len, n); 134 | DLIST_REMOVE_FROM(&free_chunk[n], chunk, free); 135 | mem_free -= len - HEADER_SIZE; 136 | } 137 | 138 | void push_free(Chunk *chunk) { 139 | size_t len = memory_chunk_size(chunk); 140 | int n = memory_chunk_slot(len); 141 | // printf("%s(%p) : adding chunk %#zx [%d]\n", __FUNCTION__, chunk, len, n); 142 | DLIST_PUSH(&free_chunk[n], chunk, free); 143 | mem_free += len - HEADER_SIZE; 144 | } 145 | 146 | void free(void *mem) { 147 | if (mem == NULL) return; 148 | Chunk *chunk = (Chunk*)((intptr_t)mem - HEADER_SIZE); 149 | Chunk *next = CONTAINER(Chunk, all, chunk->all.next); 150 | Chunk *prev = CONTAINER(Chunk, all, chunk->all.prev); 151 | printf("%s(%p): @%p %#zx [%d]\n", __FUNCTION__, mem, chunk, memory_chunk_size(chunk), memory_chunk_slot(memory_chunk_size(chunk))); 152 | mem_used -= memory_chunk_size(chunk); 153 | if (next->used == 0) { 154 | // merge in next 155 | remove_free(next); 156 | dlist_remove(&next->all); 157 | mem_meta -= HEADER_SIZE; 158 | mem_free += HEADER_SIZE; 159 | } 160 | if (prev->used == 0) { 161 | // merge to prev 162 | remove_free(prev); 163 | dlist_remove(&chunk->all); 164 | push_free(prev); 165 | mem_meta -= HEADER_SIZE; 166 | mem_free += HEADER_SIZE; 167 | } else { 168 | // make chunk as free 169 | chunk->used = 0; 170 | DLIST_INIT(chunk, free); 171 | push_free(chunk); 172 | } 173 | } 174 | 175 | void *calloc(size_t nmemb, size_t size) { 176 | // printf("# %s(%zd, %zd)\n", __FUNCTION__, nmemb, size); // delay(100000000); 177 | size = nmemb * size; 178 | void *res = malloc(size); 179 | memset(res, 0, size); 180 | return res; 181 | } 182 | 183 | void *realloc(void *ptr, size_t size) { 184 | printf("# %s(%p, %zd)\n", __FUNCTION__, ptr, size); 185 | delay(100000000); 186 | Chunk *chunk = (Chunk*)((intptr_t)ptr - HEADER_SIZE); 187 | size_t old = memory_chunk_size(chunk); 188 | printf(" old = %zd\n", old); 189 | if (old >= size) { 190 | printf("### WARNING: %s(): no shrinking\n", __FUNCTION__); 191 | return ptr; 192 | } else { 193 | void *res = malloc(size); 194 | memcpy(res, ptr, old); 195 | free(ptr); 196 | return res; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /memory.h: -------------------------------------------------------------------------------- 1 | /* memory.h - Memory management 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Manage chunks of memory implementing malloc and free. 20 | */ 21 | 22 | #ifndef OCAML_RPI__MEMORY_H 23 | #define OCAML_RPI__MEMORY_H 24 | 25 | #include 26 | #include "list.h" 27 | 28 | extern size_t mem_free; 29 | extern size_t mem_used; 30 | extern size_t mem_meta; 31 | 32 | void memory_init(void *mem, size_t size); 33 | void *malloc(size_t size); 34 | void free(void *mem); 35 | void *calloc(size_t nmemb, size_t size); 36 | void *realloc(void *ptr, size_t size); 37 | 38 | #endif // #ifndef OCAML_RPI__MEMORY_H 39 | -------------------------------------------------------------------------------- /mmio.h: -------------------------------------------------------------------------------- 1 | /* mmio.h - access to MMIO registers */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | #ifndef MMIO_H 20 | #define MMIO_H 21 | 22 | #include 23 | 24 | // write to MMIO register 25 | static inline void mmio_write(uint32_t reg, uint32_t data) { 26 | uint32_t *ptr = (uint32_t*)reg; 27 | asm volatile("str %[data], [%[reg]]" 28 | : : [reg]"r"(ptr), [data]"r"(data)); 29 | } 30 | 31 | // read from MMIO register 32 | static inline uint32_t mmio_read(uint32_t reg) { 33 | uint32_t *ptr = (uint32_t*)reg; 34 | uint32_t data; 35 | asm volatile("ldr %[data], [%[reg]]" 36 | : [data]"=r"(data) : [reg]"r"(ptr)); 37 | return data; 38 | } 39 | 40 | #endif // #ifndef MMIO_H 41 | -------------------------------------------------------------------------------- /printf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Moose Kernel - printf implementation 3 | * Copyright (C) 2007-2012 Goswin von Brederlow 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or (at 8 | * your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "uart.h" 22 | #include "printf.h" 23 | 24 | #define BUF_SIZE 4096 25 | 26 | static _Bool isdigit(unsigned char c) { 27 | return ((unsigned char)(c - '0') < 10); 28 | } 29 | 30 | ssize_t printf(const char *format, ...) { 31 | va_list args; 32 | char buf[BUF_SIZE]; 33 | ssize_t len; 34 | va_start(args, format); 35 | len = vsnprintf(buf, BUF_SIZE, format, args); 36 | va_end(args); 37 | if (len != -1 && len <= BUF_SIZE) { 38 | puts(buf); 39 | } else { 40 | // FIXME: too long 41 | panic("printf: too long\n"); 42 | } 43 | return len; 44 | } 45 | 46 | ssize_t snprintf(char *buf, size_t size, const char *format, ...) { 47 | va_list args; 48 | int len; 49 | 50 | va_start(args, format); 51 | len = vsnprintf(buf, size, format, args); 52 | va_end(args); 53 | 54 | return len; 55 | } 56 | 57 | typedef struct Flags { 58 | _Bool plus:1; // Always include a '+' or '-' sign 59 | _Bool left:1; // left justified 60 | _Bool alternate:1; // 0x prefix 61 | _Bool space:1; // space if plus 62 | _Bool zeropad:1; // pad with zero 63 | _Bool sign:1; // unsigned/signed number 64 | _Bool upper:1; // use UPPER case 65 | } Flags; 66 | 67 | /* atoi - convert string to int 68 | * @ptr: pointer to string 69 | * 70 | * Returns converted int and leaves ptr pointing to the first character after 71 | * the number. 72 | */ 73 | int atoi(const char** ptr) { 74 | const char* s = *ptr; 75 | int i = 0; 76 | while(isdigit(*s)) { 77 | i = i * 10 + (*s++ - '0'); 78 | } 79 | *ptr = s; 80 | return i; 81 | } 82 | 83 | #define buf_add(c) { \ 84 | if (buf < end) { *buf = c; } \ 85 | ++buf; \ 86 | } 87 | 88 | /* print_int - Convert integer to string 89 | * @buf: buffer 90 | * @end: end of buffer 91 | * @num: number to convert 92 | * @base: must be 10 or 16 93 | * @size: number of bytes to fill 94 | * @precision: number of digits for floats 95 | * @flags: output flags 96 | * 97 | * Returns pointer to end of string as it would be for full conversion but 98 | * only actualy writes up to excluding 'end'. 99 | */ 100 | char* sprint_int(char* buf, char* end, uint64_t num, int base, int width, int precision, Flags flags) { 101 | const char LOWER[] = "0123456789abcdef"; 102 | const char UPPER[] = "0123456789ABCDEF"; 103 | const char *digits = (flags.upper) ? UPPER : LOWER; 104 | char tmp[20]; 105 | 106 | // Sanity check base 107 | if (base != 10 && base != 16) return NULL; 108 | 109 | // Check for sign 110 | _Bool negative = false; 111 | if (flags.sign) { 112 | int64_t t = num; 113 | if (t < 0) { 114 | num = -t; 115 | negative = true; 116 | } 117 | } 118 | 119 | // convert number in reverse order 120 | int len = 0; 121 | if (num == 0) { // special case 122 | tmp[len++] = '0'; 123 | } 124 | while(num > 0) { 125 | tmp[len++] = digits[num % base]; 126 | num /= base; 127 | } 128 | // Correct presision if number too large 129 | if (precision < len) precision = len; 130 | 131 | // Account for sign and alternate form 132 | if (negative || flags.plus) { 133 | --width; 134 | } 135 | if (flags.alternate) { 136 | width -= 2; 137 | } 138 | 139 | // Put sign if any 140 | if (negative) { 141 | buf_add('-'); 142 | } else if (flags.plus) { 143 | buf_add(flags.space ? ' ' : '+'); 144 | } 145 | 146 | // Put 0x prefix 147 | if (flags.alternate) { 148 | buf_add('0'); 149 | buf_add('x'); 150 | } 151 | 152 | // Pad with ' ' if not left aligned 153 | if (!flags.left) { 154 | while(precision < width--) buf_add(flags.zeropad ? '0' : ' '); 155 | } 156 | 157 | // Pad with ' ' or '0' to precision 158 | while(len < precision--) { 159 | buf_add(flags.zeropad ? '0' : ' '); 160 | --width; 161 | } 162 | 163 | // Put number 164 | while(len > 0) { 165 | buf_add(tmp[--len]); 166 | --width; 167 | } 168 | 169 | // fill remaining space (flags.left was set) 170 | while(width-- > 0) buf_add(' '); 171 | 172 | return buf; 173 | } 174 | 175 | /* vsnprintf - Format a string and place it in a buffer 176 | * @buf: Buffer for result 177 | * @size: Size of buffer including trailing '\0' 178 | * @format: Format string for output 179 | * @args: Arguments for format string 180 | * 181 | * Returns the number of characters which would be generated for the given 182 | * input, excluding the trailing '\0', as per ISO C99. If the result is 183 | * greater than or equal to @size, the rsulting string is truncated. 184 | */ 185 | ssize_t vsnprintf(char* buf, size_t size, const char* format, va_list args) { 186 | char* start = buf; 187 | char* end = buf + size; 188 | 189 | while(*format != 0) { 190 | // Copy normal chars 1:1 191 | if (*format++ != '%') { 192 | buf_add(format[-1]); // format has already advanced 193 | continue; 194 | } 195 | 196 | // Placeholder: %[flags][width][.precision][length]type 197 | /* Flags: 198 | * '+': Always include a '+' or '-' sign for numeric types 199 | * '-': Left align output 200 | * '#': Alternate form, '0x' prefix for p and x 201 | * ' ': Include ' ' for postive numbers 202 | * '0': Pad with '0' 203 | */ 204 | Flags flags = {false, false, false, false, false, false, false}; 205 | repeat: 206 | switch(*format++) { 207 | case '+': flags.plus = true; goto repeat; 208 | case '-': flags.left = true; goto repeat; 209 | case '#': flags.alternate = true; goto repeat; 210 | case ' ': flags.space = true; goto repeat; 211 | case '0': flags.zeropad = true; goto repeat; 212 | default: --format; // undo ++ 213 | } 214 | /* Width: 215 | * '[0-9]'+: use at least this many characters 216 | * '*' : use int from 'args' as width 217 | */ 218 | int width = 0; 219 | if (*format == '*') { 220 | ++format; 221 | width = va_arg(args, int); 222 | if (width < 0) width = 0; 223 | } else if (isdigit(*format)) { 224 | width = atoi(&format); 225 | } 226 | /* Precision: 227 | * '[0-9]'+: use max this many characters for a string 228 | * '*' : use int from 'args' as precision 229 | */ 230 | int precision = -1; 231 | if (*format == '.') { 232 | ++format; 233 | if (*format == '*') { 234 | ++format; 235 | precision = va_arg(args, int); 236 | if (precision < 0) precision = 0; 237 | } else { 238 | precision = atoi(&format); 239 | } 240 | } 241 | /* Length: 242 | * 'hh': [u]int8_t 243 | * 'h' : [u]int16_t 244 | * 'l' : [u]int32_t 245 | * 'll': [u]int64_t 246 | * 'z' : [s]size_t 247 | * 't' : ptrdiff_t 248 | */ 249 | int length = 4; 250 | switch(*format++) { 251 | case 'h': 252 | if (*format == 'h') { 253 | ++format; length = 1; 254 | } else { 255 | length = sizeof(short); 256 | } 257 | break; 258 | case 'l': 259 | if (*format == 'l') { 260 | ++format; length = sizeof(long long); 261 | } else { 262 | length = sizeof(long); 263 | } 264 | break; 265 | case 'z': 266 | length = sizeof(size_t); 267 | break; 268 | case 't': 269 | length = sizeof(intptr_t); 270 | break; 271 | default: --format; // undo ++ 272 | } 273 | /* Type: 274 | * 'd', 'i': signed decimal 275 | * 'u' : unsigned decimal 276 | * 'x', 'X': unsigned hexadecimal (UPPER case) 277 | * 'p' : signed hexadecimal of a pointer 278 | * 'c' : character 279 | * 's' : string 280 | * '%' : literal '%' 281 | */ 282 | int base = 10; 283 | uint64_t num = 0; 284 | switch(*format++) { 285 | case 'd': 286 | case 'i': 287 | switch(length) { 288 | case 1: num = (int8_t) va_arg(args, int); break; 289 | case 2: num = (int16_t)va_arg(args, int); break; 290 | case 4: num = (int32_t)va_arg(args, int); break; 291 | case 8: num = (int64_t)va_arg(args, int64_t); break; 292 | } 293 | flags.sign = true; 294 | if (precision == -1) precision = 0; 295 | buf = sprint_int(buf, end, num, base, width, precision, flags); 296 | break; 297 | case 'p': 298 | flags.alternate = true; 299 | if (precision == -1) precision = 2 * sizeof(void*); 300 | case 'X': flags.upper = true; 301 | case 'x': base = 16; flags.space = false; flags.zeropad = true; 302 | case 'u': 303 | switch(length) { 304 | case 1: num = (uint8_t) va_arg(args, int); break; 305 | case 2: num = (uint16_t)va_arg(args, int); break; 306 | case 4: num = (uint32_t)va_arg(args, int); break; 307 | case 8: num = (uint64_t)va_arg(args, uint64_t); break; 308 | } 309 | if (precision == -1) precision = 0; 310 | buf = sprint_int(buf, end, num, base, width, precision, flags); 311 | break; 312 | case 'c': 313 | buf_add(va_arg(args, int)); 314 | break; 315 | case 's': { 316 | char* s = va_arg(args, char*); 317 | if (precision == -1) { 318 | while(*s != 0) { 319 | buf_add(*s++); 320 | } 321 | } else { 322 | while(precision > 0 && *s != 0) { 323 | --precision; 324 | buf_add(*s++); 325 | } 326 | } 327 | break; 328 | } 329 | case '%': 330 | buf_add('%'); 331 | break; 332 | default: // Unknown placeholder, rewind and copy '%' verbatim 333 | while(*format != '%') --format; 334 | buf_add(*format++); 335 | } 336 | } 337 | buf_add(0); 338 | if (size > 0) end[-1] = 0; // always terminate buffer if there is any 339 | return (buf - start) - 1; 340 | } 341 | -------------------------------------------------------------------------------- /printf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Moose Kernel - printf interface 3 | * Copyright (C) 2007-2008 Goswin von Brederlow 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or (at 8 | * your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef PRINTF_H 20 | #define PRINTF_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | //#ifndef ssize_t 28 | //typedef long ssize_t; 29 | //#endif 30 | 31 | #define __PRINTFLIKE(__fmt,__varargs) __attribute__((__format__ (__printf__, __fmt, __varargs))) 32 | 33 | ssize_t printf(const char *format, ...) __PRINTFLIKE(1, 2); 34 | ssize_t snprintf(char *buf, size_t size, const char *format, ...) __PRINTFLIKE(3, 4); 35 | ssize_t vsnprintf(char *buf, size_t size, const char *format, va_list args); 36 | 37 | #endif // #ifndef PRINTF_H 38 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | /* string.c - string and memory functions 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * string and memory functions 20 | */ 21 | 22 | #include "string.h" 23 | #include "printf.h" 24 | 25 | void delay(uint32_t); 26 | 27 | void *memmove(void *dest, const void *src, size_t n) { 28 | // puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 29 | char *d = (char *)dest; 30 | const char *s = (const char *)src; 31 | if (d < s || (size_t)(d - s) >= n) { 32 | while(n-- > 0) *d++ = *s++; 33 | } else { 34 | d += n; 35 | s += n; 36 | while(n-- > 0) *--d = *--s; 37 | } 38 | return dest; 39 | } 40 | 41 | void *memcpy(void *dest, const void *src, size_t n) { 42 | printf("# %s(dest=%p, src=%p, size=%zd)\n", __FUNCTION__, dest, src, n); 43 | delay(100000000); 44 | char *d = (char *)dest; 45 | const char *s = (const char *)src; 46 | while(n-- > 0) *d++ = *s++; 47 | return dest; 48 | } 49 | 50 | void *memset(void *s, int c, size_t n) { 51 | printf("# %s(s = %p, c = %#2x, n = %zd)\n", __FUNCTION__, s, c, n); 52 | // delay(100000000); 53 | char *p = (char *)s; 54 | while(n-- > 0) *p++ = c; 55 | return s; 56 | } 57 | 58 | int memcmp(const void *s1, const void *s2, size_t n) { 59 | printf("# %s(s1=%p, s2=%p, size=%zd)\n", __FUNCTION__, s1, s2, n); 60 | delay(100000000); 61 | const char *p = (const char *)s1; 62 | const char *q = (const char *)s2; 63 | int t = 0; 64 | while(t == 0 && n-- > 0) { 65 | t = *p++ - *q++; 66 | } 67 | return t; 68 | } 69 | 70 | char *strcat(char *dest, const char *src) { 71 | printf("# %s(dest=%p, src=%p)\n", __FUNCTION__, dest, src); 72 | delay(100000000); 73 | char *p = dest; 74 | while(*p++); 75 | while(*src) { *p++ = *src++; } 76 | return dest; 77 | } 78 | 79 | int strcmp(const char *s1, const char *s2) { 80 | printf("# %s(s1=%p, s2=%p)\n", __FUNCTION__, s1, s2); 81 | delay(100000000); 82 | int t = 0; 83 | while(t != 0 && *s1) { 84 | t = (*s1++) - (*s2++); 85 | } 86 | return t; 87 | } 88 | 89 | char *strcpy(char *dest, const char *src) { 90 | printf("# %s(dest=%p, src=%p)\n", __FUNCTION__, dest, src); 91 | // delay(100000000); 92 | char *p = dest; 93 | while(*src) *p++ = *src++; 94 | return dest; 95 | } 96 | 97 | size_t strlen(const char *s) { 98 | // puts("# "); puts(__FUNCTION__); puts("()\n"); // delay(100000000); 99 | size_t res = 0; 100 | while(*s++) ++res; 101 | return res; 102 | } 103 | -------------------------------------------------------------------------------- /string.h: -------------------------------------------------------------------------------- 1 | /* string.h - string and memory functions 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * string and memory functions 20 | */ 21 | 22 | #ifndef OCAML_RPI__STRING_H 23 | #define OCAML_RPI__STRING_H 24 | 25 | #include 26 | 27 | void *memmove(void *dest, const void *src, size_t n); 28 | void *memcpy(void *dest, const void *src, size_t n); 29 | void *memset(void *s, int c, size_t n); 30 | int memcmp(const void *s1, const void *s2, size_t n); 31 | char *strcat(char *dest, const char *src); 32 | int strcmp(const char *s1, const char *s2); 33 | char *strcpy(char *dest, const char *src); 34 | size_t strlen(const char *s); 35 | 36 | #endif // #ifndef OCAML_RPI__STRING_H 37 | -------------------------------------------------------------------------------- /test/list.c: -------------------------------------------------------------------------------- 1 | /* list.c - Abstract list handling test case 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Test data structures and helper functions for doubly linked cyclic lists. 20 | */ 21 | 22 | #include 23 | #include 24 | #include "../list.h" 25 | 26 | typedef struct Test { 27 | int id; 28 | DList dlist; 29 | } Test; 30 | 31 | void test_init(Test *test) { 32 | static int next_id = 0; 33 | test->id = next_id++; 34 | DLIST_INIT(test, dlist); 35 | } 36 | 37 | int main() { 38 | Test *h = NULL; 39 | Test t1, t2, t3, t4; 40 | test_init(&t1); 41 | test_init(&t2); 42 | test_init(&t3); 43 | test_init(&t4); 44 | dlist_insert_after(&t1.dlist, &t2.dlist); 45 | dlist_insert_after(&t3.dlist, &t4.dlist); 46 | DLIST_PUSH(&h, &t3, dlist); 47 | DLIST_PUSH(&h, &t1, dlist); 48 | DLIST_ITERATOR_BEGIN(h, dlist, loop) { 49 | printf("@ %p {id = %d, next = %p, prev = %p}\n", loop, loop->id, loop->dlist.next, loop->dlist.prev); 50 | if (loop->id % 2 == 0) { 51 | DLIST_REMOVE_FROM(&h, loop, dlist); 52 | } 53 | } DLIST_ITERATOR_END(loop); 54 | printf("\n"); 55 | DLIST_ITERATOR_BEGIN(h, dlist, loop) { 56 | printf("@ %p {id = %d, next = %p, prev = %p}\n", loop, loop->id, loop->dlist.next, loop->dlist.prev); 57 | } DLIST_ITERATOR_END(loop); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /test/memory.c: -------------------------------------------------------------------------------- 1 | /* memory.c - Memory management test 2 | * Copyright (C) 2013 Goswin von Brederlow 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * -- 18 | * 19 | * Test memory manager. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define OCAML_RPI__STRING_H 29 | #include "../memory.c" 30 | 31 | #define MEM_SIZE (1024*1024*1024) 32 | char MEM[MEM_SIZE] = { 0 }; 33 | 34 | #define MAX_BLOCK (1024*1024*2) 35 | #define NUM_SLOTS 1024 36 | void *slot[NUM_SLOTS] = { NULL }; 37 | size_t slot_size[NUM_SLOTS] = { 0 }; 38 | 39 | void delay(uint32_t count) { 40 | (void)count; 41 | } 42 | 43 | void check(void) { 44 | Chunk *t = last; 45 | DLIST_ITERATOR_BEGIN(first, all, it) { 46 | assert(CONTAINER(Chunk, all, it->all.prev) == t); 47 | t = it; 48 | } DLIST_ITERATOR_END(it); 49 | for(int i = 0; i < NUM_SIZES; ++i) { 50 | if (free_chunk[i]) { 51 | t = CONTAINER(Chunk, free, free_chunk[i]->free.prev); 52 | DLIST_ITERATOR_BEGIN(free_chunk[i], free, it) { 53 | assert(CONTAINER(Chunk, free, it->free.prev) == t); 54 | t = it; 55 | } DLIST_ITERATOR_END(it); 56 | } 57 | } 58 | } 59 | 60 | void fill_block(void *mem, size_t size) { 61 | // printf("%s(%p, %#zx)\n", __FUNCTION__, mem, size); 62 | void **p = (void **)mem; 63 | for(size_t i = 0; i < size / sizeof(void*); ++i) { 64 | *p++ = mem; 65 | } 66 | } 67 | 68 | void check_block(void *mem, size_t size) { 69 | // printf("%s(%p, %#zx)\n", __FUNCTION__, mem, size); 70 | void **p = (void **)mem; 71 | for(size_t i = 0; i < size / sizeof(void*); ++i) { 72 | if (p[i] != mem) { 73 | fprintf(stderr, "ERROR: memory contents changed in block %p [%#zx] @ %p\n", mem, size, &p[i]); 74 | assert(0==1); 75 | } 76 | } 77 | } 78 | 79 | int main() { 80 | printf("sizeof(DLIST) = %zd\n", sizeof(DList)); 81 | printf("HEADER_SIZE = %d\n", HEADER_SIZE); 82 | memory_init(MEM, MEM_SIZE); 83 | printf("mem_free = %#zx, mem_used = %#zx, mem_meta = %#zx\n", mem_free, mem_used, mem_meta); 84 | for(int i = 0; i < 100000000; ++i) { 85 | size_t size = random() % MAX_BLOCK; 86 | int n = random() % NUM_SLOTS; 87 | check(); 88 | if (slot[n]) { 89 | check_block(slot[n], slot_size[n]); 90 | free(slot[n]); 91 | printf("%d: mem_free = %#zx, mem_used = %#zx, mem_meta = %#zx\n", i, mem_free, mem_used, mem_meta); 92 | } 93 | check(); 94 | slot[n] = malloc(size); 95 | if (slot[n]) { 96 | slot_size[n] = size; 97 | fill_block(slot[n], slot_size[n]); 98 | } 99 | printf("%d: mem_free = %#zx, mem_used = %#zx, mem_meta = %#zx, slot[%d] = %p\n", i, mem_free, mem_used, mem_meta, n, slot[n]); 100 | } 101 | for(int i = 0; i < NUM_SLOTS; ++i) { 102 | if (slot[i]) { 103 | check(); 104 | check_block(slot[i], slot_size[i]); 105 | free(slot[i]); 106 | printf("mem_free = %#zx, mem_used = %#zx, mem_meta = %#zx\n", mem_free, mem_used, mem_meta); 107 | } 108 | } 109 | check(); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | /* uart.cc - UART initialization & communication */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | /* Reference material: 20 | * http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 21 | * Chapter 13: UART 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "mmio.h" 28 | #include "uart.h" 29 | 30 | enum { 31 | // The GPIO registers base address. 32 | GPIO_BASE = 0xE0200000, 33 | 34 | // The offsets for reach register. 35 | 36 | // Controls actuation of pull up/down to ALL GPIO pins. 37 | GPPUD = (GPIO_BASE + 0x94), 38 | 39 | // Controls actuation of pull up/down for specific GPIO pin. 40 | GPPUDCLK0 = (GPIO_BASE + 0x98), 41 | 42 | // The base address for UART. 43 | UART0_BASE = 0xE0201000, 44 | 45 | // The offsets for reach register for the UART. 46 | UART0_DR = (UART0_BASE + 0x00), 47 | UART0_RSRECR = (UART0_BASE + 0x04), 48 | UART0_FR = (UART0_BASE + 0x18), 49 | UART0_ILPR = (UART0_BASE + 0x20), 50 | UART0_IBRD = (UART0_BASE + 0x24), 51 | UART0_FBRD = (UART0_BASE + 0x28), 52 | UART0_LCRH = (UART0_BASE + 0x2C), 53 | UART0_CR = (UART0_BASE + 0x30), 54 | UART0_IFLS = (UART0_BASE + 0x34), 55 | UART0_IMSC = (UART0_BASE + 0x38), 56 | UART0_RIS = (UART0_BASE + 0x3C), 57 | UART0_MIS = (UART0_BASE + 0x40), 58 | UART0_ICR = (UART0_BASE + 0x44), 59 | UART0_DMACR = (UART0_BASE + 0x48), 60 | UART0_ITCR = (UART0_BASE + 0x80), 61 | UART0_ITIP = (UART0_BASE + 0x84), 62 | UART0_ITOP = (UART0_BASE + 0x88), 63 | UART0_TDR = (UART0_BASE + 0x8C), 64 | }; 65 | 66 | /* 67 | * delay function 68 | * int32_t delay: number of cycles to delay 69 | * 70 | * This just loops times in a way that the compiler 71 | * wont optimize away. 72 | */ 73 | void delay(int32_t count) { 74 | asm volatile("1: subs %[count], %[count], #1; bne 1b" 75 | : : [count]"r"(count)); 76 | } 77 | 78 | /* 79 | * Initialize UART0. 80 | */ 81 | void uart_init(void) { 82 | // Disable UART0. 83 | mmio_write(UART0_CR, 0x00000000); 84 | // Setup the GPIO pin 14 && 15. 85 | 86 | // Disable pull up/down for all GPIO pins & delay for 150 cycles. 87 | mmio_write(GPPUD, 0x00000000); 88 | delay(150); 89 | 90 | // Disable pull up/down for pin 14,15 & delay for 150 cycles. 91 | mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); 92 | delay(150); 93 | 94 | // Write 0 to GPPUDCLK0 to make it take effect. 95 | mmio_write(GPPUDCLK0, 0x00000000); 96 | 97 | // Clear pending interrupts. 98 | mmio_write(UART0_ICR, 0x7FF); 99 | 100 | // Set integer & fractional part of baud rate. 101 | // Divider = UART_CLOCK/(16 * Baud) 102 | // Fraction part register = (Fractional part * 64) + 0.5 103 | // UART_CLOCK = 3000000; Baud = 115200. 104 | 105 | // Divider = 3000000/(16 * 115200) = 1.627 = ~1. 106 | // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. 107 | mmio_write(UART0_IBRD, 1); 108 | mmio_write(UART0_FBRD, 40); 109 | 110 | // Enable FIFO & 8 bit data transmissio (1 stop bit, no parity). 111 | mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); 112 | 113 | // Mask all interrupts. 114 | mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | 115 | (1 << 6) | (1 << 7) | (1 << 8) | 116 | (1 << 9) | (1 << 10)); 117 | 118 | // Enable UART0, receive & transfer part of UART. 119 | mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); 120 | } 121 | 122 | /* 123 | * Transmit a character via UART0. 124 | * int c: character to send. 125 | */ 126 | int putchar(int c) { 127 | // wait for UART to become ready to transmit 128 | while(true) { 129 | if (!(mmio_read(UART0_FR) & (1 << 5))) { 130 | break; 131 | } 132 | } 133 | mmio_write(UART0_DR, c); 134 | return c; 135 | } 136 | 137 | /* 138 | * Receive a byte via UART0. 139 | * 140 | * Returns: 141 | * uint8_t: byte received. 142 | */ 143 | char getc(void) { 144 | // wait for UART to have recieved something 145 | while(true) { 146 | if (!(mmio_read(UART0_FR) & (1 << 4))) { 147 | break; 148 | } 149 | } 150 | return mmio_read(UART0_DR); 151 | } 152 | 153 | /* 154 | * print a string to the UART one character at a time 155 | * const char *str: 0-terminated string 156 | */ 157 | int puts(const char *str) { 158 | while(*str) { 159 | putchar(*str++); 160 | } 161 | return 0; 162 | } 163 | 164 | // boot.S 165 | void __attribute__((noreturn)) abort(void); 166 | 167 | void __attribute__((noreturn)) panic(const char *msg) { 168 | puts(msg); 169 | delay(100000000); 170 | abort(); 171 | } 172 | -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | /* uart.h - serial console driver */ 2 | /* Copyright (C) 2013 Goswin von Brederlow 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | /* Reference material: 20 | * http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 21 | * Chapter 13: UART 22 | */ 23 | 24 | #ifndef MOOSE_KERNEL_UART_H 25 | #define MOOSE_KERNEL_UART_H 26 | 27 | #include 28 | 29 | void uart_init(void); 30 | 31 | /* 32 | * delay function 33 | * int32_t delay: number of cycles to delay 34 | * 35 | * This just loops times in a way that the compiler 36 | * wont optimize away. 37 | */ 38 | void delay(int32_t count); 39 | 40 | /* 41 | * Receive a character via UART0. 42 | * returns: character received. 43 | */ 44 | char getc(void); 45 | 46 | /* 47 | * Check if data is available via UART0. 48 | * returns: data available? 49 | */ 50 | _Bool uart_poll(void); 51 | 52 | /* 53 | * print a character to the UART 54 | * int c: character to print 55 | */ 56 | int putchar(int c); 57 | 58 | /* 59 | * print a string to the UART one character at a time 60 | * str: 0-terminated string 61 | */ 62 | int puts(const char *str); 63 | 64 | /* 65 | * print error message to the UART and panic 66 | * msg: error message 67 | */ 68 | void __attribute__((noreturn)) panic(const char *msg); 69 | 70 | #endif // #ifndef MOOSE_KERNEL_UART_H 71 | --------------------------------------------------------------------------------