diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2009-11-04 20:58:40 +0000 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2009-11-04 20:58:40 +0000 |
commit | ea6178065c6df158db18793c72669393a37198e2 (patch) | |
tree | 9dc265025495329dff5cdd56710edc8da6e931d1 | |
parent | 33d3b54abc33e61867ec7fb3b8bc70b6f8f60615 (diff) | |
download | rockbox-ea6178065c6df158db18793c72669393a37198e2.tar.gz rockbox-ea6178065c6df158db18793c72669393a37198e2.zip |
Clean up mkamsboot building. No functional changes.
- split out standalone functions to a separate file.
- adjust and clean up Makefile.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23520 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | rbutil/mkamsboot/Makefile | 45 | ||||
-rw-r--r-- | rbutil/mkamsboot/main.c | 168 | ||||
-rw-r--r-- | rbutil/mkamsboot/mkamsboot.c | 132 | ||||
-rw-r--r-- | rbutil/mkamsboot/mkamsboot.h | 5 |
4 files changed, 199 insertions, 151 deletions
diff --git a/rbutil/mkamsboot/Makefile b/rbutil/mkamsboot/Makefile index 98a027ffb5..6cf74ee2b5 100644 --- a/rbutil/mkamsboot/Makefile +++ b/rbutil/mkamsboot/Makefile | |||
@@ -1,12 +1,13 @@ | |||
1 | # We use the UCL code available in the Rockbox tools/ directory | ||
2 | CFLAGS=-I../../tools/ucl/include -Wall | ||
3 | CC = gcc | ||
4 | 1 | ||
5 | #change for releases | 2 | #change for releases |
6 | ifndef APPVERSION | 3 | ifndef APPVERSION |
7 | APPVERSION=`../../tools/version.sh` | 4 | APPVERSION=`../../tools/version.sh` |
8 | endif | 5 | endif |
9 | 6 | ||
7 | # We use the UCL code available in the Rockbox tools/ directory | ||
8 | CFLAGS=-I../../tools/ucl/include -Wall -DVERSION=\"$(APPVERSION)\" | ||
9 | CC = gcc | ||
10 | |||
10 | ifndef V | 11 | ifndef V |
11 | SILENT = @ | 12 | SILENT = @ |
12 | endif | 13 | endif |
@@ -34,38 +35,36 @@ OUT = $(TARGET_DIR)build$(RBARCH) | |||
34 | 35 | ||
35 | all: $(OUTPUT) | 36 | all: $(OUTPUT) |
36 | 37 | ||
37 | # Dependant modules | 38 | # additional link dependencies for the standalone executable |
38 | LIBUCL=../../tools/ucl/src/libucl$(RBARCH).a | 39 | LIBUCL=../../tools/ucl/src/libucl$(RBARCH).a |
39 | 40 | ||
40 | $(LIBUCL): | 41 | $(LIBUCL): |
41 | make -C ../../tools/ucl/src $(TARGET_DIR)libucl$(RBARCH).a | 42 | make -C ../../tools/ucl/src $(TARGET_DIR)libucl$(RBARCH).a |
42 | 43 | ||
43 | # This file can be generated in the dualboot/ directory | 44 | # inputs |
44 | $(OUT)/dualboot.o: dualboot.[ch] | 45 | LIBSOURCES := dualboot.c md5.c mkamsboot.c |
45 | @echo CC $< | 46 | SOURCES := $(LIBSOURCES) main.c |
46 | $(SILENT)$(CC) $(CFLAGS) -c -o $(OUT)/dualboot.o dualboot.c | 47 | OBJS := $(patsubst %.c,%.o,$(addprefix $(OUT)/,$(SOURCES))) |
48 | LIBOBJS := $(patsubst %.c,%.o,$(addprefix $(OUT)/,$(LIBSOURCES))) | ||
49 | EXTRADEPS := $(LIBUCL) | ||
47 | 50 | ||
48 | $(OUT)/md5.o: md5.[ch] | 51 | $(OUT)/%.o: %.c $(OUT) |
49 | @echo CC $< | 52 | @echo CC $< $ |
50 | $(SILENT)$(CC) $(CFLAGS) -c -o $(OUT)/md5.o -W -Wall md5.c | 53 | $(SILENT)$(CC) $(CFLAGS) -c -o $@ $< |
51 | |||
52 | DEPENDANT_OBJS=$(LIBUCL) $(OUT)/dualboot.o $(OUT)/md5.o | ||
53 | |||
54 | $(OUT)/mkamsboot.o: mkamsboot.[ch] $(DEPENDANT_OBJS) | ||
55 | @echo CC $< | ||
56 | $(SILENT)$(CC) $(CFLAGS) -c -o $(OUT)/mkamsboot.o -W -Wall mkamsboot.c -DVERSION=\"$(APPVERSION)\" | ||
57 | |||
58 | $(OUTPUT): $(OUT) $(OUT)/mkamsboot.o | ||
59 | @echo CC $< | ||
60 | $(SILENT)$(CC) $(CFLAGS) -o $(OUTPUT) $(OUT)/mkamsboot.o $(DEPENDANT_OBJS) | ||
61 | 54 | ||
55 | # building the library archive | ||
62 | $(OUT)/libmkamsboot.o: $(OUT)/mkamsboot.o | 56 | $(OUT)/libmkamsboot.o: $(OUT)/mkamsboot.o |
63 | @echo CC $< | 57 | @echo CC $< |
64 | $(SILENT)$(CC) $(CFLAGS) -DLIB -c -o $(OUT)/libmkamsboot.o -W -Wall mkamsboot.c | 58 | $(SILENT)$(CC) $(CFLAGS) -DLIB -c -o $(OUT)/libmkamsboot.o -W -Wall mkamsboot.c |
65 | 59 | ||
66 | libmkamsboot$(RBARCH).a: $(OUT) $(OUT)/libmkamsboot.o | 60 | libmkamsboot$(RBARCH).a: $(LIBOBJS) |
67 | @echo AR $@ | 61 | @echo AR $@ |
68 | $(SILENT)$(AR) ruc $(TARGET_DIR)libmkamsboot$(RBARCH).a $(OUT)/libmkamsboot.o $(OUT)/md5.o $(OUT)/dualboot.o | 62 | $(SILENT)$(AR) ruc $(TARGET_DIR)$@ $^ |
63 | |||
64 | # building the standalone executable | ||
65 | $(OUTPUT): $(OBJS) $(EXTRADEPS) | ||
66 | @echo LD $@ | ||
67 | $(SILENT)$(CC) $(CFLAGS) -o$(OUTPUT) $(OBJS) $(EXTRADEPS) | ||
69 | 68 | ||
70 | # some trickery to build ppc and i386 from a single call | 69 | # some trickery to build ppc and i386 from a single call |
71 | ifeq ($(RBARCH),) | 70 | ifeq ($(RBARCH),) |
diff --git a/rbutil/mkamsboot/main.c b/rbutil/mkamsboot/main.c new file mode 100644 index 0000000000..5715248685 --- /dev/null +++ b/rbutil/mkamsboot/main.c | |||
@@ -0,0 +1,168 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * mkamsboot - a tool for merging bootloader code into an Sansa V2 | ||
11 | * (AMS) firmware file | ||
12 | * | ||
13 | * Copyright (C) 2008 Dave Chapman | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | |||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdint.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <fcntl.h> | ||
31 | #include <unistd.h> | ||
32 | #include <string.h> | ||
33 | |||
34 | #include <ucl/ucl.h> | ||
35 | |||
36 | #include "mkamsboot.h" | ||
37 | |||
38 | /* Header for ARM code binaries */ | ||
39 | #include "dualboot.h" | ||
40 | |||
41 | /* Win32 compatibility */ | ||
42 | #ifndef O_BINARY | ||
43 | #define O_BINARY 0 | ||
44 | #endif | ||
45 | |||
46 | /* standalone executable */ | ||
47 | int main(int argc, char* argv[]) | ||
48 | { | ||
49 | char *infile, *bootfile, *outfile; | ||
50 | int fdout; | ||
51 | off_t len; | ||
52 | uint32_t n; | ||
53 | unsigned char* buf; | ||
54 | int firmware_size; | ||
55 | int bootloader_size; | ||
56 | unsigned char* of_packed; | ||
57 | int of_packedsize; | ||
58 | unsigned char* rb_packed; | ||
59 | int rb_packedsize; | ||
60 | int totalsize; | ||
61 | char errstr[200]; | ||
62 | struct md5sums sum; | ||
63 | char md5sum[33]; /* 32 digits + \0 */ | ||
64 | |||
65 | sum.md5 = md5sum; | ||
66 | |||
67 | /* VERSION comes frome the Makefile */ | ||
68 | fprintf(stderr, | ||
69 | "mkamsboot Version " VERSION "\n" | ||
70 | "This is free software; see the source for copying conditions. There is NO\n" | ||
71 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" | ||
72 | "\n"); | ||
73 | |||
74 | if(argc != 4) { | ||
75 | printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n"); | ||
76 | return 1; | ||
77 | } | ||
78 | |||
79 | infile = argv[1]; | ||
80 | bootfile = argv[2]; | ||
81 | outfile = argv[3]; | ||
82 | |||
83 | /* Load original firmware file */ | ||
84 | buf = load_of_file(infile, &len, &sum, | ||
85 | &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr)); | ||
86 | |||
87 | if (buf == NULL) { | ||
88 | fprintf(stderr, "%s", errstr); | ||
89 | fprintf(stderr, "[ERR] Could not load %s\n", infile); | ||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | fprintf(stderr, "[INFO] Original firmware MD5 checksum match\n"); | ||
94 | fprintf(stderr, "[INFO] Model: Sansa %s v%d - Firmware version: %s\n", | ||
95 | model_names[sum.model], hw_revisions[sum.model], sum.version); | ||
96 | |||
97 | |||
98 | /* Load bootloader file */ | ||
99 | rb_packed = load_rockbox_file(bootfile, sum.model, &bootloader_size, | ||
100 | &rb_packedsize, errstr, sizeof(errstr)); | ||
101 | if (rb_packed == NULL) { | ||
102 | fprintf(stderr, "%s", errstr); | ||
103 | fprintf(stderr, "[ERR] Could not load %s\n", bootfile); | ||
104 | free(buf); | ||
105 | free(of_packed); | ||
106 | return 1; | ||
107 | } | ||
108 | |||
109 | printf("[INFO] Firmware patching has begun !\n\n"); | ||
110 | |||
111 | fprintf(stderr, "[INFO] Original firmware size: %d bytes\n", | ||
112 | firmware_size); | ||
113 | fprintf(stderr, "[INFO] Packed OF size: %d bytes\n", | ||
114 | of_packedsize); | ||
115 | fprintf(stderr, "[INFO] Bootloader size: %d bytes\n", | ||
116 | (int)bootloader_size); | ||
117 | fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n", | ||
118 | rb_packedsize); | ||
119 | fprintf(stderr, "[INFO] Dual-boot function size: %d bytes\n", | ||
120 | bootloader_sizes[sum.model]); | ||
121 | fprintf(stderr, "[INFO] UCL unpack function size: %u bytes\n", | ||
122 | (unsigned int)sizeof(nrv2e_d8)); | ||
123 | |||
124 | totalsize = total_size(sum.model, of_packedsize, rb_packedsize); | ||
125 | |||
126 | fprintf(stderr, "[INFO] Total size of new image: %d bytes\n", totalsize); | ||
127 | |||
128 | if (totalsize > firmware_size) { | ||
129 | fprintf(stderr, "[ERR] No room to insert bootloader, aborting\n"); | ||
130 | free(buf); | ||
131 | free(of_packed); | ||
132 | free(rb_packed); | ||
133 | return 1; | ||
134 | } | ||
135 | |||
136 | patch_firmware(sum.model, fw_revisions[sum.model], firmware_size, buf, len, | ||
137 | of_packed, of_packedsize, rb_packed, rb_packedsize); | ||
138 | |||
139 | /* Write the new firmware */ | ||
140 | fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); | ||
141 | |||
142 | if (fdout < 0) { | ||
143 | fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile); | ||
144 | free(buf); | ||
145 | free(of_packed); | ||
146 | free(rb_packed); | ||
147 | return 1; | ||
148 | } | ||
149 | |||
150 | n = write(fdout, buf, len); | ||
151 | |||
152 | if (n != (unsigned)len) { | ||
153 | fprintf(stderr, "[ERR] Could not write firmware file\n"); | ||
154 | free(buf); | ||
155 | free(of_packed); | ||
156 | free(rb_packed); | ||
157 | return 1; | ||
158 | } | ||
159 | |||
160 | close(fdout); | ||
161 | free(buf); | ||
162 | free(of_packed); | ||
163 | free(rb_packed); | ||
164 | fprintf(stderr, "\n[INFO] Patching succeeded!\n"); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
diff --git a/rbutil/mkamsboot/mkamsboot.c b/rbutil/mkamsboot/mkamsboot.c index 9d0ad9cd9a..8d40a1966c 100644 --- a/rbutil/mkamsboot/mkamsboot.c +++ b/rbutil/mkamsboot/mkamsboot.c | |||
@@ -110,7 +110,7 @@ execution to the uncompressed firmware. | |||
110 | #endif | 110 | #endif |
111 | 111 | ||
112 | /* 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clop */ | 112 | /* 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clop */ |
113 | static const unsigned short hw_revisions[] = { | 113 | const unsigned short hw_revisions[] = { |
114 | [MODEL_FUZE] = 1, | 114 | [MODEL_FUZE] = 1, |
115 | [MODEL_CLIP] = 1, | 115 | [MODEL_CLIP] = 1, |
116 | [MODEL_CLIPV2] = 2, | 116 | [MODEL_CLIPV2] = 2, |
@@ -120,7 +120,7 @@ static const unsigned short hw_revisions[] = { | |||
120 | }; | 120 | }; |
121 | 121 | ||
122 | /* version 2 is used in Clipv2 and Fuzev2 firmwares */ | 122 | /* version 2 is used in Clipv2 and Fuzev2 firmwares */ |
123 | static const unsigned short fw_revisions[] = { | 123 | const unsigned short fw_revisions[] = { |
124 | [MODEL_FUZE] = 1, | 124 | [MODEL_FUZE] = 1, |
125 | [MODEL_CLIP] = 1, | 125 | [MODEL_CLIP] = 1, |
126 | [MODEL_CLIPV2] = 2, | 126 | [MODEL_CLIPV2] = 2, |
@@ -130,7 +130,7 @@ static const unsigned short fw_revisions[] = { | |||
130 | }; | 130 | }; |
131 | 131 | ||
132 | /* Descriptive name of these models */ | 132 | /* Descriptive name of these models */ |
133 | static const char* model_names[] = { | 133 | const char* model_names[] = { |
134 | [MODEL_FUZE] = "Fuze", | 134 | [MODEL_FUZE] = "Fuze", |
135 | [MODEL_CLIP] = "Clip", | 135 | [MODEL_CLIP] = "Clip", |
136 | [MODEL_CLIPV2] = "Clip", | 136 | [MODEL_CLIPV2] = "Clip", |
@@ -150,7 +150,7 @@ static const unsigned char* bootloaders[] = { | |||
150 | }; | 150 | }; |
151 | 151 | ||
152 | /* Size of dualboot functions for these models */ | 152 | /* Size of dualboot functions for these models */ |
153 | static const int bootloader_sizes[] = { | 153 | const int bootloader_sizes[] = { |
154 | [MODEL_FUZE] = sizeof(dualboot_fuze), | 154 | [MODEL_FUZE] = sizeof(dualboot_fuze), |
155 | [MODEL_CLIP] = sizeof(dualboot_clip), | 155 | [MODEL_CLIP] = sizeof(dualboot_clip), |
156 | [MODEL_CLIPV2] = sizeof(dualboot_clipv2), | 156 | [MODEL_CLIPV2] = sizeof(dualboot_clipv2), |
@@ -574,127 +574,3 @@ int total_size(int model, int rb_packedsize, int of_packedsize) | |||
574 | rb_packedsize; | 574 | rb_packedsize; |
575 | } | 575 | } |
576 | 576 | ||
577 | #ifndef LIB | ||
578 | /* standalone executable */ | ||
579 | int main(int argc, char* argv[]) | ||
580 | { | ||
581 | char *infile, *bootfile, *outfile; | ||
582 | int fdout; | ||
583 | off_t len; | ||
584 | uint32_t n; | ||
585 | unsigned char* buf; | ||
586 | int firmware_size; | ||
587 | int bootloader_size; | ||
588 | unsigned char* of_packed; | ||
589 | int of_packedsize; | ||
590 | unsigned char* rb_packed; | ||
591 | int rb_packedsize; | ||
592 | int totalsize; | ||
593 | char errstr[200]; | ||
594 | struct md5sums sum; | ||
595 | char md5sum[33]; /* 32 digits + \0 */ | ||
596 | |||
597 | sum.md5 = md5sum; | ||
598 | |||
599 | /* VERSION comes frome the Makefile */ | ||
600 | fprintf(stderr, | ||
601 | "mkamsboot Version " VERSION "\n" | ||
602 | "This is free software; see the source for copying conditions. There is NO\n" | ||
603 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" | ||
604 | "\n"); | ||
605 | |||
606 | if(argc != 4) { | ||
607 | printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n"); | ||
608 | return 1; | ||
609 | } | ||
610 | |||
611 | infile = argv[1]; | ||
612 | bootfile = argv[2]; | ||
613 | outfile = argv[3]; | ||
614 | |||
615 | /* Load original firmware file */ | ||
616 | buf = load_of_file(infile, &len, &sum, | ||
617 | &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr)); | ||
618 | |||
619 | if (buf == NULL) { | ||
620 | fprintf(stderr, "%s", errstr); | ||
621 | fprintf(stderr, "[ERR] Could not load %s\n", infile); | ||
622 | return 1; | ||
623 | } | ||
624 | |||
625 | fprintf(stderr, "[INFO] Original firmware MD5 checksum match\n"); | ||
626 | fprintf(stderr, "[INFO] Model: Sansa %s v%d - Firmware version: %s\n", | ||
627 | model_names[sum.model], hw_revisions[sum.model], sum.version); | ||
628 | |||
629 | |||
630 | /* Load bootloader file */ | ||
631 | rb_packed = load_rockbox_file(bootfile, sum.model, &bootloader_size, | ||
632 | &rb_packedsize, errstr, sizeof(errstr)); | ||
633 | if (rb_packed == NULL) { | ||
634 | fprintf(stderr, "%s", errstr); | ||
635 | fprintf(stderr, "[ERR] Could not load %s\n", bootfile); | ||
636 | free(buf); | ||
637 | free(of_packed); | ||
638 | return 1; | ||
639 | } | ||
640 | |||
641 | printf("[INFO] Firmware patching has begun !\n\n"); | ||
642 | |||
643 | fprintf(stderr, "[INFO] Original firmware size: %d bytes\n", | ||
644 | firmware_size); | ||
645 | fprintf(stderr, "[INFO] Packed OF size: %d bytes\n", | ||
646 | of_packedsize); | ||
647 | fprintf(stderr, "[INFO] Bootloader size: %d bytes\n", | ||
648 | (int)bootloader_size); | ||
649 | fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n", | ||
650 | rb_packedsize); | ||
651 | fprintf(stderr, "[INFO] Dual-boot function size: %d bytes\n", | ||
652 | bootloader_sizes[sum.model]); | ||
653 | fprintf(stderr, "[INFO] UCL unpack function size: %u bytes\n", | ||
654 | (unsigned int)sizeof(nrv2e_d8)); | ||
655 | |||
656 | totalsize = total_size(sum.model, of_packedsize, rb_packedsize); | ||
657 | |||
658 | fprintf(stderr, "[INFO] Total size of new image: %d bytes\n", totalsize); | ||
659 | |||
660 | if (totalsize > firmware_size) { | ||
661 | fprintf(stderr, "[ERR] No room to insert bootloader, aborting\n"); | ||
662 | free(buf); | ||
663 | free(of_packed); | ||
664 | free(rb_packed); | ||
665 | return 1; | ||
666 | } | ||
667 | |||
668 | patch_firmware(sum.model, fw_revisions[sum.model], firmware_size, buf, len, | ||
669 | of_packed, of_packedsize, rb_packed, rb_packedsize); | ||
670 | |||
671 | /* Write the new firmware */ | ||
672 | fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); | ||
673 | |||
674 | if (fdout < 0) { | ||
675 | fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile); | ||
676 | free(buf); | ||
677 | free(of_packed); | ||
678 | free(rb_packed); | ||
679 | return 1; | ||
680 | } | ||
681 | |||
682 | n = write(fdout, buf, len); | ||
683 | |||
684 | if (n != (unsigned)len) { | ||
685 | fprintf(stderr, "[ERR] Could not write firmware file\n"); | ||
686 | free(buf); | ||
687 | free(of_packed); | ||
688 | free(rb_packed); | ||
689 | return 1; | ||
690 | } | ||
691 | |||
692 | close(fdout); | ||
693 | free(buf); | ||
694 | free(of_packed); | ||
695 | free(rb_packed); | ||
696 | fprintf(stderr, "\n[INFO] Patching succeeded!\n"); | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | #endif | ||
diff --git a/rbutil/mkamsboot/mkamsboot.h b/rbutil/mkamsboot/mkamsboot.h index 51f5b2f699..647770ff46 100644 --- a/rbutil/mkamsboot/mkamsboot.h +++ b/rbutil/mkamsboot/mkamsboot.h | |||
@@ -51,6 +51,11 @@ struct md5sums { | |||
51 | char *md5; | 51 | char *md5; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | extern const unsigned short hw_revisions[]; | ||
55 | extern const unsigned short fw_revisions[]; | ||
56 | extern const char* model_names[]; | ||
57 | extern const int bootloader_sizes[]; | ||
58 | |||
54 | /* load_rockbox_file() | 59 | /* load_rockbox_file() |
55 | * | 60 | * |
56 | * Loads a rockbox bootloader file into memory | 61 | * Loads a rockbox bootloader file into memory |