Subversion Repositories Tronxy-X3A-Marlin

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 ron 1
/**
2
 * Marlin 3D Printer Firmware
3
 * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
 *
5
 * Based on Sprinter and grbl.
6
 * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22
 
23
/**
24
 * M100 Free Memory Watcher
25
 *
26
 * This code watches the free memory block between the bottom of the heap and the top of the stack.
27
 * This memory block is initialized and watched via the M100 command.
28
 *
29
 * M100 I   Initializes the free memory block and prints vitals statistics about the area
30
 *
31
 * M100 F   Identifies how much of the free memory block remains free and unused. It also
32
 *          detects and reports any corruption within the free memory block that may have
33
 *          happened due to errant firmware.
34
 *
35
 * M100 D   Does a hex display of the free memory block along with a flag for any errant
36
 *          data that does not match the expected value.
37
 *
38
 * M100 C x Corrupts x locations within the free memory block. This is useful to check the
39
 *          correctness of the M100 F and M100 D commands.
40
 *
41
 * Also, there are two support functions that can be called from a developer's C code.
42
 *
43
 *    uint16_t check_for_free_memory_corruption(const char * const ptr);
44
 *    void M100_dump_routine(const char * const title, const char *start, const char *end);
45
 *
46
 * Initial version by Roxy-3D
47
 */
48
 
49
#include "MarlinConfig.h"
50
 
51
#if ENABLED(M100_FREE_MEMORY_WATCHER)
52
 
53
#define M100_FREE_MEMORY_DUMPER     // Enable for the `M100 D` Dump sub-command
54
#define M100_FREE_MEMORY_CORRUPTOR  // Enable for the `M100 C` Corrupt sub-command
55
 
56
#include "Marlin.h"
57
#include "parser.h"
58
#include "hex_print_routines.h"
59
 
60
#define TEST_BYTE ((char) 0xE5)
61
 
62
extern char* __brkval;
63
extern size_t  __heap_start, __heap_end, __flp;
64
extern char __bss_end;
65
 
66
//
67
// Utility functions
68
//
69
 
70
#define END_OF_HEAP() (__brkval ? __brkval : &__bss_end)
71
int check_for_free_memory_corruption(const char * const title);
72
 
73
// Location of a variable on its stack frame. Returns a value above
74
// the stack (once the function returns to the caller).
75
char* top_of_stack() {
76
  char x;
77
  return &x + 1; // x is pulled on return;
78
}
79
 
80
// Count the number of test bytes at the specified location.
81
int16_t count_test_bytes(const char * const ptr) {
82
  for (uint16_t i = 0; i < 32000; i++)
83
    if (((char) ptr[i]) != TEST_BYTE)
84
      return i - 1;
85
 
86
  return -1;
87
}
88
 
89
//
90
// M100 sub-commands
91
//
92
 
93
#if ENABLED(M100_FREE_MEMORY_DUMPER)
94
  /**
95
   * M100 D
96
   *  Dump the free memory block from __brkval to the stack pointer.
97
   *  malloc() eats memory from the start of the block and the stack grows
98
   *  up from the bottom of the block. Solid test bytes indicate nothing has
99
   *  used that memory yet. There should not be anything but test bytes within
100
   *  the block. If so, it may indicate memory corruption due to a bad pointer.
101
   *  Unexpected bytes are flagged in the right column.
102
   */
103
  void dump_free_memory(const char *ptr, const char *sp) {
104
    //
105
    // Start and end the dump on a nice 16 byte boundary
106
    // (even though the values are not 16-byte aligned).
107
    //
108
    ptr = (char *)((uint16_t)ptr & 0xFFF0); // Align to 16-byte boundary
109
    sp  = (char *)((uint16_t)sp  | 0x000F); // Align sp to the 15th byte (at or above sp)
110
 
111
    // Dump command main loop
112
    while (ptr < sp) {
113
      print_hex_word((uint16_t)ptr);      // Print the address
114
      SERIAL_CHAR(':');
115
      for (uint8_t i = 0; i < 16; i++) {  // and 16 data bytes
116
        if (i == 8) SERIAL_CHAR('-');
117
        print_hex_byte(ptr[i]);
118
        SERIAL_CHAR(' ');
119
      }
120
      safe_delay(25);
121
      SERIAL_CHAR('|');                   // Point out non test bytes
122
      for (uint8_t i = 0; i < 16; i++) {
123
        char ccc = (char)ptr[i]; // cast to char before automatically casting to char on assignment, in case the compiler is broken
124
        if (&ptr[i] >= (const char*)command_queue && &ptr[i] < (const char*)(command_queue + sizeof(command_queue))) { // Print out ASCII in the command buffer area
125
          if (!WITHIN(ccc, ' ', 0x7E)) ccc = ' ';
126
        }
127
        else { // If not in the command buffer area, flag bytes that don't match the test byte
128
          ccc = (ccc == TEST_BYTE) ? ' ' : '?';
129
        }
130
        SERIAL_CHAR(ccc);
131
      }
132
      SERIAL_EOL();
133
      ptr += 16;
134
      safe_delay(25);
135
      idle();
136
    }
137
  }
138
 
139
void M100_dump_routine(const char * const title, const char *start, const char *end) {
140
  SERIAL_ECHOLN(title);
141
  //
142
  // Round the start and end locations to produce full lines of output
143
  //
144
  start = (char*)((uint16_t) start & 0xFFF0);
145
  end   = (char*)((uint16_t) end   | 0x000F);
146
  dump_free_memory(start, end);
147
}
148
 
149
#endif // M100_FREE_MEMORY_DUMPER
150
 
151
/**
152
 * M100 F
153
 *  Return the number of free bytes in the memory pool,
154
 *  with other vital statistics defining the pool.
155
 */
156
void free_memory_pool_report(char * const ptr, const int16_t size) {
157
  int16_t max_cnt = -1, block_cnt = 0;
158
  char *max_addr = NULL;
159
  // Find the longest block of test bytes in the buffer
160
  for (int16_t i = 0; i < size; i++) {
161
    char *addr = ptr + i;
162
    if (*addr == TEST_BYTE) {
163
      const int16_t j = count_test_bytes(addr);
164
      if (j > 8) {
165
        SERIAL_ECHOPAIR("Found ", j);
166
        SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
167
        if (j > max_cnt) {
168
          max_cnt  = j;
169
          max_addr = addr;
170
        }
171
        i += j;
172
        block_cnt++;
173
      }
174
    }
175
  }
176
  if (block_cnt > 1) {
177
    SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
178
    SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
179
    SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
180
  }
181
  SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption("M100 F "));
182
}
183
 
184
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
185
  /**
186
   * M100 C<num>
187
   *  Corrupt <num> locations in the free memory pool and report the corrupt addresses.
188
   *  This is useful to check the correctness of the M100 D and the M100 F commands.
189
   */
190
  void corrupt_free_memory(char *ptr, const uint16_t size) {
191
    ptr += 8;
192
    const uint16_t near_top = top_of_stack() - ptr - 250, // -250 to avoid interrupt activity that's altered the stack.
193
                   j = near_top / (size + 1);
194
 
195
    SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
196
    for (uint16_t i = 1; i <= size; i++) {
197
      char * const addr = ptr + i * j;
198
      *addr = i;
199
      SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
200
    }
201
    SERIAL_EOL();
202
  }
203
#endif // M100_FREE_MEMORY_CORRUPTOR
204
 
205
/**
206
 * M100 I
207
 *  Init memory for the M100 tests. (Automatically applied on the first M100.)
208
 */
209
void init_free_memory(char *ptr, int16_t size) {
210
  SERIAL_ECHOLNPGM("Initializing free memory block.\n\n");
211
 
212
  size -= 250;    // -250 to avoid interrupt activity that's altered the stack.
213
  if (size < 0) {
214
    SERIAL_ECHOLNPGM("Unable to initialize.\n");
215
    return;
216
  }
217
 
218
  ptr += 8;       // move a few bytes away from the heap just because we don't want
219
                  // to be altering memory that close to it.
220
  memset(ptr, TEST_BYTE, size);
221
 
222
  SERIAL_ECHO(size);
223
  SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
224
 
225
  for (int16_t i = 0; i < size; i++) {
226
    if (ptr[i] != TEST_BYTE) {
227
      SERIAL_ECHOPAIR("? address : ", hex_address(ptr + i));
228
      SERIAL_ECHOLNPAIR("=", hex_byte(ptr[i]));
229
      SERIAL_EOL();
230
    }
231
  }
232
}
233
 
234
/**
235
 * M100: Free Memory Check
236
 */
237
void gcode_M100() {
238
  SERIAL_ECHOPAIR("\n__brkval : ", hex_address(__brkval));
239
  SERIAL_ECHOPAIR("\n__bss_end : ", hex_address(&__bss_end));
240
 
241
  char *ptr = END_OF_HEAP(), *sp = top_of_stack();
242
 
243
  SERIAL_ECHOPAIR("\nstart of free space : ", hex_address(ptr));
244
  SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
245
 
246
  // Always init on the first invocation of M100
247
  static bool m100_not_initialized = true;
248
  if (m100_not_initialized || parser.seen('I')) {
249
    m100_not_initialized = false;
250
    init_free_memory(ptr, sp - ptr);
251
  }
252
 
253
  #if ENABLED(M100_FREE_MEMORY_DUMPER)
254
    if (parser.seen('D'))
255
      return dump_free_memory(ptr, sp);
256
  #endif
257
 
258
  if (parser.seen('F'))
259
    return free_memory_pool_report(ptr, sp - ptr);
260
 
261
  #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
262
 
263
    if (parser.seen('C'))
264
      return corrupt_free_memory(ptr, parser.value_int());
265
 
266
  #endif
267
}
268
 
269
int check_for_free_memory_corruption(const char * const title) {
270
  SERIAL_ECHO(title);
271
 
272
  char *ptr = END_OF_HEAP(), *sp = top_of_stack();
273
  int n = sp - ptr;
274
 
275
  SERIAL_ECHOPAIR("\nfmc() n=", n);
276
  SERIAL_ECHOPAIR("\n&__brkval: ", hex_address(&__brkval));
277
  SERIAL_ECHOPAIR("=",             hex_address(__brkval));
278
  SERIAL_ECHOPAIR("\n__bss_end: ", hex_address(&__bss_end));
279
  SERIAL_ECHOPAIR(" sp=",          hex_address(sp));
280
 
281
  if (sp < ptr)  {
282
    SERIAL_ECHOPGM(" sp < Heap ");
283
    // SET_INPUT_PULLUP(63);           // if the developer has a switch wired up to their controller board
284
    // safe_delay(5);                  // this code can be enabled to pause the display as soon as the
285
    // while ( READ(63))               // malfunction is detected.   It is currently defaulting to a switch
286
    //   idle();                       // being on pin-63 which is unassigend and available on most controller
287
    // safe_delay(20);                 // boards.
288
    // while ( !READ(63))
289
    //   idle();
290
    safe_delay(20);
291
    #ifdef M100_FREE_MEMORY_DUMPER
292
      M100_dump_routine("   Memory corruption detected with sp<Heap\n", (char*)0x1B80, (char*)0x21FF);
293
    #endif
294
  }
295
 
296
  // Scan through the range looking for the biggest block of 0xE5's we can find
297
  int block_cnt = 0;
298
  for (int i = 0; i < n; i++) {
299
    if (ptr[i] == TEST_BYTE) {
300
      int16_t j = count_test_bytes(ptr + i);
301
      if (j > 8) {
302
        // SERIAL_ECHOPAIR("Found ", j);
303
        // SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(ptr + i));
304
        i += j;
305
        block_cnt++;
306
        SERIAL_ECHOPAIR(" (", block_cnt);
307
        SERIAL_ECHOPAIR(") found=", j);
308
        SERIAL_ECHOPGM("   ");
309
      }
310
    }
311
  }
312
  SERIAL_ECHOPAIR("  block_found=", block_cnt);
313
 
314
  if (block_cnt != 1 || __brkval != 0x0000)
315
    SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
316
 
317
  if (block_cnt == 0)       // Make sure the special case of no free blocks shows up as an
318
    block_cnt = -1;         // error to the calling code!
319
 
320
  SERIAL_ECHOPGM(" return=");
321
  if (block_cnt == 1) {
322
    SERIAL_CHAR('0');       // if the block_cnt is 1, nothing has broken up the free memory
323
    SERIAL_EOL();             // area and it is appropriate to say 'no corruption'.
324
    return 0;
325
  }
326
  SERIAL_ECHOLNPGM("true");
327
  return block_cnt;
328
}
329
 
330
#endif // M100_FREE_MEMORY_WATCHER
331
 
332