Changeset e731b0d in mainline for boot/arch/sparc64
- Timestamp:
- 2009-08-20T16:58:55Z (16 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- b9c7425
- Parents:
- a11099f
- Location:
- boot/arch/sparc64/loader
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
boot/arch/sparc64/loader/asm.S
ra11099f re731b0d 31 31 #include <register.h> 32 32 33 .register 34 .register 33 .register %g2, #scratch 34 .register %g3, #scratch 35 35 36 36 .text … … 43 43 ba %xcc, halt 44 44 nop 45 46 memcpy: 47 mov %o0, %o3 ! save dst 48 add %o1, 7, %g1 49 and %g1, -8, %g1 50 cmp %o1, %g1 51 be,pn %xcc, 3f 52 add %o0, 7, %g1 53 mov 0, %g3 45 54 46 memcpy: 47 mov %o0, %o3 ! save dst 48 add %o1, 7, %g1 49 and %g1, -8, %g1 50 cmp %o1, %g1 51 be,pn %xcc, 3f 52 add %o0, 7, %g1 53 mov 0, %g3 54 0: 55 brz,pn %o2, 2f 56 mov 0, %g2 57 1: 58 ldub [%g3 + %o1], %g1 59 add %g2, 1, %g2 60 cmp %o2, %g2 61 stb %g1, [%g3 + %o0] 62 bne,pt %xcc, 1b 63 mov %g2, %g3 64 2: 65 jmp %o7 + 8 ! exit point 66 mov %o3, %o0 67 3: 68 and %g1, -8, %g1 69 cmp %o0, %g1 70 bne,pt %xcc, 0b 71 mov 0, %g3 72 srlx %o2, 3, %g4 73 brz,pn %g4, 5f 74 mov 0, %g5 75 4: 76 sllx %g3, 3, %g2 77 add %g5, 1, %g3 78 ldx [%o1 + %g2], %g1 79 mov %g3, %g5 80 cmp %g4, %g3 81 bne,pt %xcc, 4b 82 stx %g1, [%o0 + %g2] 83 5: 84 and %o2, 7, %o2 85 brz,pn %o2, 2b 86 sllx %g4, 3, %g1 87 mov 0, %g2 88 add %g1, %o0, %o0 89 add %g1, %o1, %g4 90 mov 0, %g3 91 6: 92 ldub [%g2 + %g4], %g1 93 stb %g1, [%g2 + %o0] 94 add %g3, 1, %g2 95 cmp %o2, %g2 96 bne,pt %xcc, 6b 97 mov %g2, %g3 98 99 jmp %o7 + 8 ! exit point 100 mov %o3, %o0 55 0: 56 brz,pn %o2, 2f 57 mov 0, %g2 58 59 1: 60 ldub [%g3 + %o1], %g1 61 add %g2, 1, %g2 62 cmp %o2, %g2 63 stb %g1, [%g3 + %o0] 64 bne,pt %xcc, 1b 65 mov %g2, %g3 66 67 2: 68 jmp %o7 + 8 ! exit point 69 mov %o3, %o0 70 71 3: 72 and %g1, -8, %g1 73 cmp %o0, %g1 74 bne,pt %xcc, 0b 75 mov 0, %g3 76 srlx %o2, 3, %g4 77 brz,pn %g4, 5f 78 mov 0, %g5 79 80 4: 81 sllx %g3, 3, %g2 82 add %g5, 1, %g3 83 ldx [%o1 + %g2], %g1 84 mov %g3, %g5 85 cmp %g4, %g3 86 bne,pt %xcc, 4b 87 stx %g1, [%o0 + %g2] 88 89 5: 90 and %o2, 7, %o2 91 brz,pn %o2, 2b 92 sllx %g4, 3, %g1 93 mov 0, %g2 94 add %g1, %o0, %o0 95 add %g1, %o1, %g4 96 mov 0, %g3 97 98 6: 99 ldub [%g2 + %g4], %g1 100 stb %g1, [%g2 + %o0] 101 add %g3, 1, %g2 102 cmp %o2, %g2 103 bne,pt %xcc, 6b 104 mov %g2, %g3 105 106 jmp %o7 + 8 ! exit point 107 mov %o3, %o0 101 108 102 109 jump_to_kernel: … … 107 114 * 3. Flush instruction pipeline. 108 115 */ 109 116 110 117 /* 111 118 * US3 processors have a write-invalidate cache, so explicitly 112 119 * invalidating it is not required. Whether to invalidate I-cache 113 * or not is decided according to the value of the global114 * "subarchitecture" variable (set in the bootstrap).120 * or not is decided according to the value of the 5th argument 121 * (subarchitecture). 115 122 */ 116 set subarchitecture, %g2 117 ldub [%g2], %g2 118 cmp %g2, 3 123 cmp %i4, 3 119 124 be %xcc, 1f 120 125 nop 121 0: 122 call icache_flush 123 nop 124 1: 125 membar #StoreStore 126 127 0: 128 call icache_flush 129 nop 130 131 1: 132 membar #StoreStore 126 133 127 134 /* 128 135 * Flush the instruction pipeline. 129 136 */ 130 flush 131 137 flush %i7 138 132 139 mov %o0, %l1 133 140 mov %o1, %o0 134 141 mov %o2, %o1 135 142 mov %o3, %o2 136 jmp %l1 143 jmp %l1 ! jump to kernel 137 144 nop 138 145 139 #define ICACHE_SIZE 140 #define ICACHE_LINE_SIZE 141 #define ICACHE_SET_BIT 142 #define ASI_ICACHE_TAG 146 #define ICACHE_SIZE 8192 147 #define ICACHE_LINE_SIZE 32 148 #define ICACHE_SET_BIT (1 << 13) 149 #define ASI_ICACHE_TAG 0x67 143 150 144 151 # Flush I-cache 145 152 icache_flush: 146 set ((ICACHE_SIZE - ICACHE_LINE_SIZE) | ICACHE_SET_BIT), %g1 147 stxa %g0, [%g1] ASI_ICACHE_TAG 148 0: membar #Sync 149 subcc %g1, ICACHE_LINE_SIZE, %g1 150 bnz,pt %xcc, 0b 151 stxa %g0, [%g1] ASI_ICACHE_TAG 152 membar #Sync 153 set ((ICACHE_SIZE - ICACHE_LINE_SIZE) | ICACHE_SET_BIT), %g1 154 stxa %g0, [%g1] ASI_ICACHE_TAG 155 156 0: 157 membar #Sync 158 subcc %g1, ICACHE_LINE_SIZE, %g1 159 bnz,pt %xcc, 0b 160 161 stxa %g0, [%g1] ASI_ICACHE_TAG 162 membar #Sync 153 163 retl 154 164 ! SF Erratum #51 155 165 nop 166 156 167 .global ofw 157 168 ofw: … … 159 170 set ofw_cif, %l0 160 171 ldx [%l0], %l0 161 172 162 173 rdpr %pstate, %l1 163 174 and %l1, ~PSTATE_AM_BIT, %l2 164 175 wrpr %l2, 0, %pstate 165 176 166 177 jmpl %l0, %o7 167 178 mov %i0, %o0 168 179 169 180 wrpr %l1, 0, %pstate 170 181 171 182 ret 172 183 restore %o0, 0, %o0 -
boot/arch/sparc64/loader/asm.h
ra11099f re731b0d 34 34 #include "main.h" 35 35 36 #define PAGE_WIDTH 37 #define PAGE_SIZE 36 #define PAGE_WIDTH 14 37 #define PAGE_SIZE (1 << PAGE_WIDTH) 38 38 39 #define memcpy(dst, src, cnt) __builtin_memcpy((dst), (src), (cnt))39 #define BALLOC_MAX_SIZE (128 * 1024) 40 40 41 41 extern void halt(void); 42 42 extern void jump_to_kernel(void *entry, uint64_t cfg, bootinfo_t *bootinfo, 43 unsigned int bootinfo_size) __attribute__((noreturn));43 unsigned int bootinfo_size, uint8_t subarchitecture) __attribute__((noreturn)); 44 44 45 45 #endif -
boot/arch/sparc64/loader/main.c
ra11099f re731b0d 1 1 /* 2 2 * Copyright (c) 2005 Martin Decky 3 * Copyright (c) 2006 Jakub Jermar 3 * Copyright (c) 2006 Jakub Jermar 4 4 * All rights reserved. 5 5 * … … 28 28 */ 29 29 30 #include "main.h" 30 #include "main.h" 31 31 #include <printf.h> 32 32 #include "asm.h" … … 39 39 #include <macros.h> 40 40 #include <string.h> 41 42 bootinfo_t bootinfo; 43 44 component_t components[COMPONENTS]; 45 46 char *release = STRING(RELEASE); 41 #include <memstr.h> 42 43 static bootinfo_t bootinfo; 44 static component_t components[COMPONENTS]; 45 static char *release = STRING(RELEASE); 47 46 48 47 #ifdef REVISION 49 char *revision = ", revision " STRING(REVISION);48 static char *revision = ", revision " STRING(REVISION); 50 49 #else 51 char *revision = "";50 static char *revision = ""; 52 51 #endif 53 52 54 53 #ifdef TIMESTAMP 55 char *timestamp = "\nBuilt on " STRING(TIMESTAMP);54 static char *timestamp = "\nBuilt on " STRING(TIMESTAMP); 56 55 #else 57 char *timestamp = "";56 static char *timestamp = ""; 58 57 #endif 59 58 60 59 /** UltraSPARC subarchitecture - 1 for US, 3 for US3 */ 61 uint8_t subarchitecture;60 static uint8_t subarchitecture; 62 61 63 62 /** … … 65 64 * MID_SHIFT bits to the right 66 65 */ 67 uint16_t mid_mask;66 static uint16_t mid_mask; 68 67 69 68 /** Print version information. */ … … 76 75 77 76 /* the lowest ID (read from the VER register) of some US3 CPU model */ 78 #define FIRST_US3_CPU 77 #define FIRST_US3_CPU 0x14 79 78 80 79 /* the greatest ID (read from the VER register) of some US3 CPU model */ 81 #define LAST_US3_CPU 80 #define LAST_US3_CPU 0x19 82 81 83 82 /* UltraSPARC IIIi processor implementation code */ 84 #define US_IIIi_CODE 83 #define US_IIIi_CODE 0x15 85 84 86 85 /** … … 91 90 { 92 91 uint64_t v; 93 asm volatile ("rdpr %%ver, %0\n" : "=r" (v)); 92 asm volatile ( 93 "rdpr %%ver, %0\n" 94 : "=r" (v) 95 ); 94 96 95 97 v = (v << 16) >> 48; … … 103 105 subarchitecture = SUBARCH_US; 104 106 mid_mask = (1 << 5) - 1; 105 } else {107 } else 106 108 printf("\nThis CPU is not supported by HelenOS."); 107 }108 109 } 109 110 … … 113 114 void *balloc_base; 114 115 unsigned int top = 0; 115 int i, j; 116 116 unsigned int i; 117 unsigned int j; 118 117 119 version_print(); 118 120 119 121 detect_subarchitecture(); 120 122 init_components(components); 121 123 122 124 if (!ofw_get_physmem_start(&bootinfo.physmem_start)) { 123 125 printf("Error: unable to get start of physical memory.\n"); 124 126 halt(); 125 127 } 126 128 127 129 if (!ofw_memmap(&bootinfo.memmap)) { 128 130 printf("Error: unable to get memory map, halting.\n"); 129 131 halt(); 130 132 } 131 133 132 134 if (bootinfo.memmap.total == 0) { 133 135 printf("Error: no memory detected, halting.\n"); 134 136 halt(); 135 137 } 136 138 137 139 /* 138 140 * SILO for some reason adds 0x400000 and subtracts … … 143 145 silo_ramdisk_image += bootinfo.physmem_start; 144 146 silo_ramdisk_image -= 0x400000; 145 /* Install 1:1 mapping for the ramdisk. */ 146 if (ofw_map((void *)((uintptr_t) silo_ramdisk_image), 147 (void *)((uintptr_t) silo_ramdisk_image), 147 148 /* Install 1:1 mapping for the RAM disk. */ 149 if (ofw_map((void *) ((uintptr_t) silo_ramdisk_image), 150 (void *) ((uintptr_t) silo_ramdisk_image), 148 151 silo_ramdisk_size, -1) != 0) { 149 printf("Failed to map ramdisk.\n");152 printf("Failed to map RAM disk.\n"); 150 153 halt(); 151 154 } 152 155 } 153 156 154 printf("\nSystem info\n"); 155 printf(" memory: %dM starting at %P\n", 157 printf("\nMemory statistics (total %d MB, starting at %P)\n", 156 158 bootinfo.memmap.total >> 20, bootinfo.physmem_start); 157 158 printf("\nMemory statistics\n"); 159 printf(" kernel entry point at %P\n", KERNEL_VIRTUAL_ADDRESS); 159 printf(" %P: kernel entry point\n", KERNEL_VIRTUAL_ADDRESS); 160 160 printf(" %P: boot info structure\n", &bootinfo); 161 161 … … 176 176 break; 177 177 } 178 178 179 bootinfo.taskmap.tasks[bootinfo.taskmap.count].addr = 179 180 base + top; … … 187 188 top += components[i].size; 188 189 } 189 190 j = bootinfo.taskmap.count - 1; /* do not consider ramdisk */ 191 190 191 /* Do not consider RAM disk */ 192 j = bootinfo.taskmap.count - 1; 193 192 194 if (silo_ramdisk_image) { 193 /* Treat the ramdisk as the last bootinfo task. */195 /* Treat the RAM disk as the last bootinfo task. */ 194 196 if (bootinfo.taskmap.count == TASKMAP_MAX_RECORDS) { 195 printf("Skipping ramdisk.\n");197 printf("Skipping RAM disk.\n"); 196 198 goto skip_ramdisk; 197 199 } 200 198 201 top = ALIGN_UP(top, PAGE_SIZE); 199 202 bootinfo.taskmap.tasks[bootinfo.taskmap.count].addr = … … 202 205 silo_ramdisk_size; 203 206 bootinfo.taskmap.count++; 204 printf("\nCopying ramdisk..."); 207 printf("\nCopying RAM disk..."); 208 205 209 /* 206 210 * Claim and map the whole ramdisk as it may exceed the area … … 210 214 (void) ofw_map(bootinfo.physmem_start + base + top, base + top, 211 215 silo_ramdisk_size, -1); 212 memmove(base + top, (void *) ((uintptr_t)silo_ramdisk_image),216 memmove(base + top, (void *) ((uintptr_t) silo_ramdisk_image), 213 217 silo_ramdisk_size); 218 214 219 printf("done.\n"); 215 220 top += silo_ramdisk_size; 216 221 } 217 222 skip_ramdisk: 218 223 219 224 /* 220 225 * Now we can proceed to copy the components. We do it in reverse order … … 222 227 * with base. 223 228 */ 224 printf("\nCopying bootinfo tasks\n");229 printf("\nCopying tasks..."); 225 230 for (i = COMPONENTS - 1; i > 0; i--, j--) { 226 printf(" %s...", components[i].name);227 231 printf("%s ", components[i].name); 232 228 233 /* 229 234 * At this point, we claim the physical memory that we are … … 240 245 bootinfo.taskmap.tasks[j].addr, 241 246 ALIGN_UP(components[i].size, PAGE_SIZE)); 242 243 memcpy((void *) bootinfo.taskmap.tasks[j].addr,247 248 memcpy((void *) bootinfo.taskmap.tasks[j].addr, 244 249 components[i].start, components[i].size); 245 printf("done.\n"); 246 } 247 250 251 } 252 printf(".\n"); 253 248 254 printf("\nCopying kernel..."); 249 255 (void) ofw_claim_phys(bootinfo.physmem_start + base, … … 251 257 memcpy(base, components[0].start, components[0].size); 252 258 printf("done.\n"); 253 259 254 260 /* 255 261 * Claim and map the physical memory for the boot allocator. … … 261 267 (void) ofw_map(bootinfo.physmem_start + balloc_base, balloc_base, 262 268 BALLOC_MAX_SIZE, -1); 263 balloc_init(&bootinfo.ballocs, (uintptr_t)balloc_base); 264 269 balloc_init(&bootinfo.ballocs, (uintptr_t) balloc_base, 270 (uintptr_t) balloc_base); 271 265 272 printf("\nCanonizing OpenFirmware device tree..."); 266 273 bootinfo.ofw_root = ofw_tree_build(); 267 274 printf("done.\n"); 268 275 269 276 #ifdef CONFIG_AP 270 277 printf("\nChecking for secondary processors..."); 271 if (!ofw_cpu( ))278 if (!ofw_cpu(mid_mask, bootinfo.physmem_start)) 272 279 printf("Error: unable to get CPU properties\n"); 273 280 printf("done.\n"); 274 281 #endif 275 282 276 283 ofw_setup_palette(); 277 284 278 285 printf("\nBooting the kernel...\n"); 279 286 jump_to_kernel((void *) KERNEL_VIRTUAL_ADDRESS, 280 287 bootinfo.physmem_start | BSP_PROCESSOR, &bootinfo, 281 sizeof(bootinfo) );288 sizeof(bootinfo), subarchitecture); 282 289 } -
boot/arch/sparc64/loader/main.h
ra11099f re731b0d 35 35 #include <types.h> 36 36 37 #define KERNEL_VIRTUAL_ADDRESS 0x40000037 #define KERNEL_VIRTUAL_ADDRESS 0x400000 38 38 39 #define TASKMAP_MAX_RECORDS 3239 #define TASKMAP_MAX_RECORDS 32 40 40 41 41 /** Size of buffer for storing task name in task_t. */ 42 #define BOOTINFO_TASK_NAME_BUFLEN 3242 #define BOOTINFO_TASK_NAME_BUFLEN 32 43 43 44 #define BSP_PROCESSOR 45 #define AP_PROCESSOR 44 #define BSP_PROCESSOR 1 45 #define AP_PROCESSOR 0 46 46 47 #define SUBARCH_US 48 #define SUBARCH_US3 47 #define SUBARCH_US 1 48 #define SUBARCH_US3 3 49 49 50 50 typedef struct { … … 70 70 extern uint32_t silo_ramdisk_size; 71 71 72 extern bootinfo_t bootinfo;73 74 72 extern void start(void); 75 73 extern void bootstrap(void); -
boot/arch/sparc64/loader/ofwarch.c
ra11099f re731b0d 30 30 /** 31 31 * @file 32 * @brief 32 * @brief Architecture dependent parts of OpenFirmware interface. 33 33 */ 34 34 … … 40 40 #include "main.h" 41 41 #include "asm.h" 42 43 /* these tho variables will be set by the detect_subarchitecture function */44 extern uint8_t subarchitecture;45 extern uint16_t mid_mask;46 42 47 43 void write(const char *str, const int len) … … 65 61 * except for the current CPU. 66 62 * 67 * @param child The first child of the OFW tree node whose children 68 * represent CPUs to be woken up. 69 * @param current_mid MID of the current CPU, the current CPU will 70 * (of course) not be woken up. 71 * @return Number of CPUs which have the same parent node as 72 * "child". 63 * @param child The first child of the OFW tree node whose children 64 * represent CPUs to be woken up. 65 * @param current_mid MID of the current CPU, the current CPU will 66 * (of course) not be woken up. 67 * @param physmem_start Starting address of the physical memory. 68 * 69 * @return Number of CPUs which have the same parent node as 70 * "child". 71 * 73 72 */ 74 static int wake_cpus_in_node(phandle child, uint64_t current_mid) 73 static int wake_cpus_in_node(phandle child, uint64_t current_mid, 74 uintptr_t physmem_start) 75 75 { 76 76 int cpus; 77 char type_name[BUF_SIZE];78 77 79 for (cpus = 0; child != 0 && child != -1;78 for (cpus = 0; (child != 0) && (child != -1); 80 79 child = ofw_get_peer_node(child), cpus++) { 80 char type_name[BUF_SIZE]; 81 81 82 if (ofw_get_property(child, "device_type", type_name, 82 83 sizeof(type_name)) > 0) { … … 88 89 * "cpuid" for US-IV 89 90 */ 90 if (ofw_get_property( 91 child, "upa-portid", 92 &mid, sizeof(mid)) <= 0 93 && ofw_get_property(child, "portid", 94 &mid, sizeof(mid)) <= 0 95 && ofw_get_property(child, "cpuid", 96 &mid, sizeof(mid)) <= 0) 91 if ((ofw_get_property(child, "upa-portid", &mid, sizeof(mid)) <= 0) 92 && (ofw_get_property(child, "portid", &mid, sizeof(mid)) <= 0) 93 && (ofw_get_property(child, "cpuid", &mid, sizeof(mid)) <= 0)) 97 94 continue; 98 95 99 96 if (current_mid != mid) { 100 97 /* … … 103 100 (void) ofw_call("SUNW,start-cpu", 3, 1, 104 101 NULL, child, KERNEL_VIRTUAL_ADDRESS, 105 bootinfo.physmem_start | 106 AP_PROCESSOR); 102 physmem_start | AP_PROCESSOR); 107 103 } 108 104 } 109 105 } 110 106 } 111 107 112 108 return cpus; 113 109 } … … 116 112 * Finds out the current CPU's MID and wakes up all AP processors. 117 113 */ 118 int ofw_cpu( void)114 int ofw_cpu(uint16_t mid_mask, uintptr_t physmem_start) 119 115 { 120 int cpus; 121 phandle node; 122 phandle subnode; 123 phandle cpus_parent; 124 phandle cmp; 125 char name[BUF_SIZE]; 126 127 /* get the current CPU MID */ 116 /* Get the current CPU MID */ 128 117 uint64_t current_mid; 129 118 130 asm volatile ("ldxa [%1] %2, %0\n" 131 : "=r" (current_mid) 132 : "r" (0), "i" (ASI_ICBUS_CONFIG)); 119 asm volatile ( 120 "ldxa [%1] %2, %0\n" 121 : "=r" (current_mid) 122 : "r" (0), "i" (ASI_ICBUS_CONFIG) 123 ); 124 133 125 current_mid >>= ICBUS_CONFIG_MID_SHIFT; 134 135 126 current_mid &= mid_mask; 136 137 /* wake up CPUs */138 127 139 cpus_parent = ofw_find_device("/ssm@0,0"); 140 if (cpus_parent == 0 || cpus_parent == -1) { 128 /* Wake up the CPUs */ 129 130 phandle cpus_parent = ofw_find_device("/ssm@0,0"); 131 if ((cpus_parent == 0) || (cpus_parent == -1)) 141 132 cpus_parent = ofw_find_device("/"); 142 } 143 144 node = ofw_get_child_node(cpus_parent); 145 cpus = wake_cpus_in_node(node, current_mid); 146 while (node != 0 && node != -1) { 133 134 phandle node = ofw_get_child_node(cpus_parent); 135 int cpus = wake_cpus_in_node(node, current_mid, physmem_start); 136 while ((node != 0) && (node != -1)) { 137 char name[BUF_SIZE]; 138 147 139 if (ofw_get_property(node, "name", name, 148 140 sizeof(name)) > 0) { 149 141 if (strcmp(name, "cmp") == 0) { 150 subnode = ofw_get_child_node(node);142 phandle subnode = ofw_get_child_node(node); 151 143 cpus += wake_cpus_in_node(subnode, 152 current_mid );144 current_mid, physmem_start); 153 145 } 154 146 } … … 157 149 158 150 return cpus; 159 160 151 } 161 152 162 153 /** Get physical memory starting address. 163 154 * 164 * @param start 165 * 155 * @param start Pointer to variable where the physical memory starting 156 * address will be stored. 166 157 * 167 * @return Non-zero on succes, zero on failure. 158 * @return Non-zero on succes, zero on failure. 159 * 168 160 */ 169 161 int ofw_get_physmem_start(uintptr_t *start) 170 162 { 171 163 uint32_t memreg[4]; 172 173 164 if (ofw_get_property(ofw_memory, "reg", &memreg, sizeof(memreg)) <= 0) 174 165 return 0; 175 166 176 167 *start = (((uint64_t) memreg[0]) << 32) | memreg[1]; 177 168 return 1; 178 169 } 179 -
boot/arch/sparc64/loader/ofwarch.h
ra11099f re731b0d 33 33 #include "types.h" 34 34 35 #define OFW_ADDRESS_CELLS 36 #define OFW_SIZE_CELLS 35 #define OFW_ADDRESS_CELLS 2 36 #define OFW_SIZE_CELLS 2 37 37 38 extern int ofw_cpu( void);39 extern int ofw_get_physmem_start(uintptr_t *start); 38 extern int ofw_cpu(uint16_t mid_mask, uintptr_t physmem_start); 39 extern int ofw_get_physmem_start(uintptr_t *start); 40 40 41 41 #endif
Note:
See TracChangeset
for help on using the changeset viewer.