diff options
-rw-r--r-- | arch/x86/kernel/microcode_amd.c | 43 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_core.c | 58 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_intel.c | 83 |
3 files changed, 97 insertions, 87 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index c25fdb382292..453b5795a5c6 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c | |||
@@ -12,31 +12,30 @@ | |||
12 | * | 12 | * |
13 | * Licensed under the terms of the GNU General Public | 13 | * Licensed under the terms of the GNU General Public |
14 | * License version 2. See file COPYING for details. | 14 | * License version 2. See file COPYING for details. |
15 | */ | 15 | */ |
16 | 16 | #include <linux/platform_device.h> | |
17 | #include <linux/capability.h> | 17 | #include <linux/capability.h> |
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/cpumask.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/vmalloc.h> | ||
25 | #include <linux/miscdevice.h> | 18 | #include <linux/miscdevice.h> |
19 | #include <linux/firmware.h> | ||
26 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
27 | #include <linux/mm.h> | 21 | #include <linux/cpumask.h> |
28 | #include <linux/fs.h> | 22 | #include <linux/pci_ids.h> |
23 | #include <linux/uaccess.h> | ||
24 | #include <linux/vmalloc.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
29 | #include <linux/mutex.h> | 27 | #include <linux/mutex.h> |
28 | #include <linux/sched.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/slab.h> | ||
30 | #include <linux/cpu.h> | 31 | #include <linux/cpu.h> |
31 | #include <linux/firmware.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/pci.h> | 32 | #include <linux/pci.h> |
34 | #include <linux/pci_ids.h> | 33 | #include <linux/fs.h> |
35 | #include <linux/uaccess.h> | 34 | #include <linux/mm.h> |
36 | 35 | ||
37 | #include <asm/msr.h> | ||
38 | #include <asm/processor.h> | ||
39 | #include <asm/microcode.h> | 36 | #include <asm/microcode.h> |
37 | #include <asm/processor.h> | ||
38 | #include <asm/msr.h> | ||
40 | 39 | ||
41 | MODULE_DESCRIPTION("AMD Microcode Update Driver"); | 40 | MODULE_DESCRIPTION("AMD Microcode Update Driver"); |
42 | MODULE_AUTHOR("Peter Oruba"); | 41 | MODULE_AUTHOR("Peter Oruba"); |
@@ -72,8 +71,8 @@ struct microcode_header_amd { | |||
72 | } __attribute__((packed)); | 71 | } __attribute__((packed)); |
73 | 72 | ||
74 | struct microcode_amd { | 73 | struct microcode_amd { |
75 | struct microcode_header_amd hdr; | 74 | struct microcode_header_amd hdr; |
76 | unsigned int mpb[0]; | 75 | unsigned int mpb[0]; |
77 | }; | 76 | }; |
78 | 77 | ||
79 | #define UCODE_MAX_SIZE 2048 | 78 | #define UCODE_MAX_SIZE 2048 |
@@ -184,8 +183,8 @@ static int get_ucode_data(void *to, const u8 *from, size_t n) | |||
184 | return 0; | 183 | return 0; |
185 | } | 184 | } |
186 | 185 | ||
187 | static void *get_next_ucode(const u8 *buf, unsigned int size, | 186 | static void * |
188 | unsigned int *mc_size) | 187 | get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size) |
189 | { | 188 | { |
190 | unsigned int total_size; | 189 | unsigned int total_size; |
191 | u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; | 190 | u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; |
@@ -223,7 +222,6 @@ static void *get_next_ucode(const u8 *buf, unsigned int size, | |||
223 | return mc; | 222 | return mc; |
224 | } | 223 | } |
225 | 224 | ||
226 | |||
227 | static int install_equiv_cpu_table(const u8 *buf) | 225 | static int install_equiv_cpu_table(const u8 *buf) |
228 | { | 226 | { |
229 | u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; | 227 | u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; |
@@ -372,4 +370,3 @@ struct microcode_ops * __init init_amd_microcode(void) | |||
372 | { | 370 | { |
373 | return µcode_amd_ops; | 371 | return µcode_amd_ops; |
374 | } | 372 | } |
375 | |||
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 9a8dbc000563..a0f3851ef310 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c | |||
@@ -70,47 +70,47 @@ | |||
70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. | 70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. |
71 | * Thanks to Stuart Swales for pointing out this bug. | 71 | * Thanks to Stuart Swales for pointing out this bug. |
72 | */ | 72 | */ |
73 | #include <linux/platform_device.h> | ||
73 | #include <linux/capability.h> | 74 | #include <linux/capability.h> |
74 | #include <linux/kernel.h> | 75 | #include <linux/miscdevice.h> |
75 | #include <linux/init.h> | 76 | #include <linux/firmware.h> |
76 | #include <linux/sched.h> | ||
77 | #include <linux/smp_lock.h> | 77 | #include <linux/smp_lock.h> |
78 | #include <linux/spinlock.h> | ||
78 | #include <linux/cpumask.h> | 79 | #include <linux/cpumask.h> |
79 | #include <linux/module.h> | 80 | #include <linux/uaccess.h> |
80 | #include <linux/slab.h> | ||
81 | #include <linux/vmalloc.h> | 81 | #include <linux/vmalloc.h> |
82 | #include <linux/miscdevice.h> | 82 | #include <linux/kernel.h> |
83 | #include <linux/spinlock.h> | 83 | #include <linux/module.h> |
84 | #include <linux/mm.h> | ||
85 | #include <linux/fs.h> | ||
86 | #include <linux/mutex.h> | 84 | #include <linux/mutex.h> |
85 | #include <linux/sched.h> | ||
86 | #include <linux/init.h> | ||
87 | #include <linux/slab.h> | ||
87 | #include <linux/cpu.h> | 88 | #include <linux/cpu.h> |
88 | #include <linux/firmware.h> | 89 | #include <linux/fs.h> |
89 | #include <linux/platform_device.h> | 90 | #include <linux/mm.h> |
90 | 91 | ||
91 | #include <asm/msr.h> | ||
92 | #include <asm/uaccess.h> | ||
93 | #include <asm/processor.h> | ||
94 | #include <asm/microcode.h> | 92 | #include <asm/microcode.h> |
93 | #include <asm/processor.h> | ||
94 | #include <asm/msr.h> | ||
95 | 95 | ||
96 | MODULE_DESCRIPTION("Microcode Update Driver"); | 96 | MODULE_DESCRIPTION("Microcode Update Driver"); |
97 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); | 97 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); |
98 | MODULE_LICENSE("GPL"); | 98 | MODULE_LICENSE("GPL"); |
99 | 99 | ||
100 | #define MICROCODE_VERSION "2.00" | 100 | #define MICROCODE_VERSION "2.00" |
101 | 101 | ||
102 | static struct microcode_ops *microcode_ops; | 102 | static struct microcode_ops *microcode_ops; |
103 | 103 | ||
104 | /* no concurrent ->write()s are allowed on /dev/cpu/microcode */ | 104 | /* no concurrent ->write()s are allowed on /dev/cpu/microcode */ |
105 | static DEFINE_MUTEX(microcode_mutex); | 105 | static DEFINE_MUTEX(microcode_mutex); |
106 | 106 | ||
107 | struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; | 107 | struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; |
108 | EXPORT_SYMBOL_GPL(ucode_cpu_info); | 108 | EXPORT_SYMBOL_GPL(ucode_cpu_info); |
109 | 109 | ||
110 | #ifdef CONFIG_MICROCODE_OLD_INTERFACE | 110 | #ifdef CONFIG_MICROCODE_OLD_INTERFACE |
111 | struct update_for_cpu { | 111 | struct update_for_cpu { |
112 | const void __user *buf; | 112 | const void __user *buf; |
113 | size_t size; | 113 | size_t size; |
114 | }; | 114 | }; |
115 | 115 | ||
116 | static long update_for_cpu(void *_ufc) | 116 | static long update_for_cpu(void *_ufc) |
@@ -209,12 +209,12 @@ static void microcode_dev_exit(void) | |||
209 | 209 | ||
210 | MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); | 210 | MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); |
211 | #else | 211 | #else |
212 | #define microcode_dev_init() 0 | 212 | #define microcode_dev_init() 0 |
213 | #define microcode_dev_exit() do { } while (0) | 213 | #define microcode_dev_exit() do { } while (0) |
214 | #endif | 214 | #endif |
215 | 215 | ||
216 | /* fake device for request_firmware */ | 216 | /* fake device for request_firmware */ |
217 | static struct platform_device *microcode_pdev; | 217 | static struct platform_device *microcode_pdev; |
218 | 218 | ||
219 | static long reload_for_cpu(void *unused) | 219 | static long reload_for_cpu(void *unused) |
220 | { | 220 | { |
@@ -282,8 +282,8 @@ static struct attribute *mc_default_attrs[] = { | |||
282 | }; | 282 | }; |
283 | 283 | ||
284 | static struct attribute_group mc_attr_group = { | 284 | static struct attribute_group mc_attr_group = { |
285 | .attrs = mc_default_attrs, | 285 | .attrs = mc_default_attrs, |
286 | .name = "microcode", | 286 | .name = "microcode", |
287 | }; | 287 | }; |
288 | 288 | ||
289 | static void __microcode_fini_cpu(int cpu) | 289 | static void __microcode_fini_cpu(int cpu) |
@@ -353,7 +353,7 @@ static long microcode_update_cpu(void *unused) | |||
353 | */ | 353 | */ |
354 | if (uci->valid) { | 354 | if (uci->valid) { |
355 | err = microcode_resume_cpu(smp_processor_id()); | 355 | err = microcode_resume_cpu(smp_processor_id()); |
356 | } else { | 356 | } else { |
357 | collect_cpu_info(smp_processor_id()); | 357 | collect_cpu_info(smp_processor_id()); |
358 | if (uci->valid && system_state == SYSTEM_RUNNING) | 358 | if (uci->valid && system_state == SYSTEM_RUNNING) |
359 | err = microcode_ops->request_microcode_fw( | 359 | err = microcode_ops->request_microcode_fw( |
@@ -423,9 +423,9 @@ static int mc_sysdev_resume(struct sys_device *dev) | |||
423 | } | 423 | } |
424 | 424 | ||
425 | static struct sysdev_driver mc_sysdev_driver = { | 425 | static struct sysdev_driver mc_sysdev_driver = { |
426 | .add = mc_sysdev_add, | 426 | .add = mc_sysdev_add, |
427 | .remove = mc_sysdev_remove, | 427 | .remove = mc_sysdev_remove, |
428 | .resume = mc_sysdev_resume, | 428 | .resume = mc_sysdev_resume, |
429 | }; | 429 | }; |
430 | 430 | ||
431 | static __cpuinit int | 431 | static __cpuinit int |
@@ -464,7 +464,7 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) | |||
464 | } | 464 | } |
465 | 465 | ||
466 | static struct notifier_block __refdata mc_cpu_notifier = { | 466 | static struct notifier_block __refdata mc_cpu_notifier = { |
467 | .notifier_call = mc_cpu_callback, | 467 | .notifier_call = mc_cpu_callback, |
468 | }; | 468 | }; |
469 | 469 | ||
470 | static int __init microcode_init(void) | 470 | static int __init microcode_init(void) |
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c index 5e9f4fc51385..149b9ec7c1ab 100644 --- a/arch/x86/kernel/microcode_intel.c +++ b/arch/x86/kernel/microcode_intel.c | |||
@@ -70,28 +70,28 @@ | |||
70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. | 70 | * Fix sigmatch() macro to handle old CPUs with pf == 0. |
71 | * Thanks to Stuart Swales for pointing out this bug. | 71 | * Thanks to Stuart Swales for pointing out this bug. |
72 | */ | 72 | */ |
73 | #include <linux/platform_device.h> | ||
73 | #include <linux/capability.h> | 74 | #include <linux/capability.h> |
74 | #include <linux/kernel.h> | 75 | #include <linux/miscdevice.h> |
75 | #include <linux/init.h> | 76 | #include <linux/firmware.h> |
76 | #include <linux/sched.h> | ||
77 | #include <linux/smp_lock.h> | 77 | #include <linux/smp_lock.h> |
78 | #include <linux/spinlock.h> | ||
78 | #include <linux/cpumask.h> | 79 | #include <linux/cpumask.h> |
79 | #include <linux/module.h> | 80 | #include <linux/uaccess.h> |
80 | #include <linux/slab.h> | ||
81 | #include <linux/vmalloc.h> | 81 | #include <linux/vmalloc.h> |
82 | #include <linux/miscdevice.h> | 82 | #include <linux/kernel.h> |
83 | #include <linux/spinlock.h> | 83 | #include <linux/module.h> |
84 | #include <linux/mm.h> | ||
85 | #include <linux/fs.h> | ||
86 | #include <linux/mutex.h> | 84 | #include <linux/mutex.h> |
85 | #include <linux/sched.h> | ||
86 | #include <linux/init.h> | ||
87 | #include <linux/slab.h> | ||
87 | #include <linux/cpu.h> | 88 | #include <linux/cpu.h> |
88 | #include <linux/firmware.h> | 89 | #include <linux/fs.h> |
89 | #include <linux/platform_device.h> | 90 | #include <linux/mm.h> |
90 | #include <linux/uaccess.h> | ||
91 | 91 | ||
92 | #include <asm/msr.h> | ||
93 | #include <asm/processor.h> | ||
94 | #include <asm/microcode.h> | 92 | #include <asm/microcode.h> |
93 | #include <asm/processor.h> | ||
94 | #include <asm/msr.h> | ||
95 | 95 | ||
96 | MODULE_DESCRIPTION("Microcode Update Driver"); | 96 | MODULE_DESCRIPTION("Microcode Update Driver"); |
97 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); | 97 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); |
@@ -129,12 +129,13 @@ struct extended_sigtable { | |||
129 | struct extended_signature sigs[0]; | 129 | struct extended_signature sigs[0]; |
130 | }; | 130 | }; |
131 | 131 | ||
132 | #define DEFAULT_UCODE_DATASIZE (2000) | 132 | #define DEFAULT_UCODE_DATASIZE (2000) |
133 | #define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) | 133 | #define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) |
134 | #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) | 134 | #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) |
135 | #define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) | 135 | #define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) |
136 | #define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) | 136 | #define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) |
137 | #define DWSIZE (sizeof(u32)) | 137 | #define DWSIZE (sizeof(u32)) |
138 | |||
138 | #define get_totalsize(mc) \ | 139 | #define get_totalsize(mc) \ |
139 | (((struct microcode_intel *)mc)->hdr.totalsize ? \ | 140 | (((struct microcode_intel *)mc)->hdr.totalsize ? \ |
140 | ((struct microcode_intel *)mc)->hdr.totalsize : \ | 141 | ((struct microcode_intel *)mc)->hdr.totalsize : \ |
@@ -197,30 +198,31 @@ static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf) | |||
197 | } | 198 | } |
198 | 199 | ||
199 | static inline int | 200 | static inline int |
200 | update_match_revision(struct microcode_header_intel *mc_header, int rev) | 201 | update_match_revision(struct microcode_header_intel *mc_header, int rev) |
201 | { | 202 | { |
202 | return (mc_header->rev <= rev) ? 0 : 1; | 203 | return (mc_header->rev <= rev) ? 0 : 1; |
203 | } | 204 | } |
204 | 205 | ||
205 | static int microcode_sanity_check(void *mc) | 206 | static int microcode_sanity_check(void *mc) |
206 | { | 207 | { |
208 | unsigned long total_size, data_size, ext_table_size; | ||
207 | struct microcode_header_intel *mc_header = mc; | 209 | struct microcode_header_intel *mc_header = mc; |
208 | struct extended_sigtable *ext_header = NULL; | 210 | struct extended_sigtable *ext_header = NULL; |
209 | struct extended_signature *ext_sig; | ||
210 | unsigned long total_size, data_size, ext_table_size; | ||
211 | int sum, orig_sum, ext_sigcount = 0, i; | 211 | int sum, orig_sum, ext_sigcount = 0, i; |
212 | struct extended_signature *ext_sig; | ||
212 | 213 | ||
213 | total_size = get_totalsize(mc_header); | 214 | total_size = get_totalsize(mc_header); |
214 | data_size = get_datasize(mc_header); | 215 | data_size = get_datasize(mc_header); |
216 | |||
215 | if (data_size + MC_HEADER_SIZE > total_size) { | 217 | if (data_size + MC_HEADER_SIZE > total_size) { |
216 | printk(KERN_ERR "microcode: error! " | 218 | printk(KERN_ERR "microcode: error! " |
217 | "Bad data size in microcode data file\n"); | 219 | "Bad data size in microcode data file\n"); |
218 | return -EINVAL; | 220 | return -EINVAL; |
219 | } | 221 | } |
220 | 222 | ||
221 | if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { | 223 | if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { |
222 | printk(KERN_ERR "microcode: error! " | 224 | printk(KERN_ERR "microcode: error! " |
223 | "Unknown microcode update format\n"); | 225 | "Unknown microcode update format\n"); |
224 | return -EINVAL; | 226 | return -EINVAL; |
225 | } | 227 | } |
226 | ext_table_size = total_size - (MC_HEADER_SIZE + data_size); | 228 | ext_table_size = total_size - (MC_HEADER_SIZE + data_size); |
@@ -318,11 +320,15 @@ get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev) | |||
318 | 320 | ||
319 | static void apply_microcode(int cpu) | 321 | static void apply_microcode(int cpu) |
320 | { | 322 | { |
323 | struct microcode_intel *mc_intel; | ||
324 | struct ucode_cpu_info *uci; | ||
321 | unsigned long flags; | 325 | unsigned long flags; |
322 | unsigned int val[2]; | 326 | unsigned int val[2]; |
323 | int cpu_num = raw_smp_processor_id(); | 327 | int cpu_num; |
324 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 328 | |
325 | struct microcode_intel *mc_intel = uci->mc; | 329 | cpu_num = raw_smp_processor_id(); |
330 | uci = ucode_cpu_info + cpu; | ||
331 | mc_intel = uci->mc; | ||
326 | 332 | ||
327 | /* We should bind the task to the CPU */ | 333 | /* We should bind the task to the CPU */ |
328 | BUG_ON(cpu_num != cpu); | 334 | BUG_ON(cpu_num != cpu); |
@@ -348,15 +354,17 @@ static void apply_microcode(int cpu) | |||
348 | spin_unlock_irqrestore(µcode_update_lock, flags); | 354 | spin_unlock_irqrestore(µcode_update_lock, flags); |
349 | if (val[1] != mc_intel->hdr.rev) { | 355 | if (val[1] != mc_intel->hdr.rev) { |
350 | printk(KERN_ERR "microcode: CPU%d update from revision " | 356 | printk(KERN_ERR "microcode: CPU%d update from revision " |
351 | "0x%x to 0x%x failed\n", cpu_num, uci->cpu_sig.rev, val[1]); | 357 | "0x%x to 0x%x failed\n", |
358 | cpu_num, uci->cpu_sig.rev, val[1]); | ||
352 | return; | 359 | return; |
353 | } | 360 | } |
354 | printk(KERN_INFO "microcode: CPU%d updated from revision " | 361 | printk(KERN_INFO "microcode: CPU%d updated from revision " |
355 | "0x%x to 0x%x, date = %04x-%02x-%02x \n", | 362 | "0x%x to 0x%x, date = %04x-%02x-%02x \n", |
356 | cpu_num, uci->cpu_sig.rev, val[1], | 363 | cpu_num, uci->cpu_sig.rev, val[1], |
357 | mc_intel->hdr.date & 0xffff, | 364 | mc_intel->hdr.date & 0xffff, |
358 | mc_intel->hdr.date >> 24, | 365 | mc_intel->hdr.date >> 24, |
359 | (mc_intel->hdr.date >> 16) & 0xff); | 366 | (mc_intel->hdr.date >> 16) & 0xff); |
367 | |||
360 | uci->cpu_sig.rev = val[1]; | 368 | uci->cpu_sig.rev = val[1]; |
361 | } | 369 | } |
362 | 370 | ||
@@ -404,18 +412,23 @@ static int generic_load_microcode(int cpu, void *data, size_t size, | |||
404 | leftover -= mc_size; | 412 | leftover -= mc_size; |
405 | } | 413 | } |
406 | 414 | ||
407 | if (new_mc) { | 415 | if (!new_mc) |
408 | if (!leftover) { | 416 | goto out; |
409 | if (uci->mc) | 417 | |
410 | vfree(uci->mc); | 418 | if (leftover) { |
411 | uci->mc = (struct microcode_intel *)new_mc; | 419 | vfree(new_mc); |
412 | pr_debug("microcode: CPU%d found a matching microcode update with" | 420 | goto out; |
413 | " version 0x%x (current=0x%x)\n", | ||
414 | cpu, new_rev, uci->cpu_sig.rev); | ||
415 | } else | ||
416 | vfree(new_mc); | ||
417 | } | 421 | } |
418 | 422 | ||
423 | if (uci->mc) | ||
424 | vfree(uci->mc); | ||
425 | uci->mc = (struct microcode_intel *)new_mc; | ||
426 | |||
427 | pr_debug("microcode: CPU%d found a matching microcode update with" | ||
428 | " version 0x%x (current=0x%x)\n", | ||
429 | cpu, new_rev, uci->cpu_sig.rev); | ||
430 | |||
431 | out: | ||
419 | return (int)leftover; | 432 | return (int)leftover; |
420 | } | 433 | } |
421 | 434 | ||