xref: /aosp_15_r20/external/coreboot/src/drivers/wifi/generic/mtcl.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <acpi/acpigen.h>
4 #include <cbfs.h>
5 #include <mtcl.h>
6 #include <stdint.h>
7 #include <string.h>
8 
9 #define WIFI_MTCL_CBFS_DEFAULT_FILENAME "wifi_mtcl.bin"
10 #define MAX_VERSION 2
11 #define MAX_SUPPORT_STATE 2
12 #define COUNTRY_LIST_SIZE 6
13 #define NAME_SIZE 4
14 #define MTCL_NAME "MTCL"
15 
16 /*
17  * Represent the structured MTCL data.
18  * This struct is used to cast from an array of uint8_t in order to help
19  * understand the semantic purpose of individual bytes. This struct is used in
20  * order to verify that the bytes included match the known MTCL data format.
21  * This struct is explicitly __packed because it is explicitly cast to from an
22  * array of uint8_t.
23  */
24 struct wifi_mtcl {
25 	uint8_t name[NAME_SIZE];
26 	uint8_t revision;
27 	uint8_t support_6ghz;
28 	uint8_t country_list_6ghz[COUNTRY_LIST_SIZE];
29 	uint8_t support_5p9ghz;
30 	uint8_t country_list_5p9ghz[COUNTRY_LIST_SIZE];
31 } __packed;
32 
33 void write_mtcl_aml(uint8_t *bytes, size_t count);
34 int validate_mtcl(uint8_t *mtcl_bytes, size_t count);
35 
36 /*
37  * Generate ACPI AML code for MTCL method.
38  * This function takes as input an array of bytes that correspond to the value
39  * map to be passed as a package, as well as the count of bytes to be written.
40  *
41  * AML code generate would look like:
42  * Method(MTCL, 0, Serialized)
43  * {
44  *   Name (LIST, Package()
45  *   {
46  *     // data table
47  *   })
48  *   Return (LIST)
49  * }
50  */
write_mtcl_aml(uint8_t * bytes,size_t count)51 void write_mtcl_aml(uint8_t *bytes, size_t count)
52 {
53 	/* Method (MTCL, 0, Serialized) */
54 	acpigen_write_method_serialized("MTCL", 0x0);
55 
56 	/* Name (LIST */
57 	acpigen_write_name("LIST");
58 
59 	/* Package () */
60 	acpigen_write_package(count);
61 
62 	/* Write the provided bytes. */
63 	for (int i = 0; i < count; ++i)
64 		acpigen_write_byte(bytes[i]);
65 
66 	acpigen_write_package_end(); /* Package */
67 
68 	/* Return MTCL */
69 	acpigen_write_return_namestr("LIST");
70 	acpigen_write_method_end();  /* Method MTCL */
71 }
72 
73 /*
74  * Validate the WiFi MTCL data that is passed in from CBFS.
75  *
76  * Data is expected in the format:
77  *   [Revision,
78  *     6GHz Support,
79  *     6GHz Country List,
80  *     5.9GHz Support,
81  *     5.9GHz Country List]
82  *
83  * The revision is expected to be "2".
84  *
85  * 6GHz support is a byte with the following states:
86  *   - 0 - 6GHz operation disabled
87  *   - 1 - 6GHz operation dictated by the country list and Operating System
88  *   - 2 - 6GHz operation dictated by the Operating System
89  *
90  * 6GHz Country List is a set of 6 bytes that represent a bitmask of countries
91  * in which 6GHz operation is enabled.
92  *
93  * 5.9GHz Support is a byte with the following known states:
94  *   - 0 - 5.9GHz operation disabled
95  *   - 1 - 5.9GHz operation dictated by the country list and Operating System
96  *   - 2 - 5.9GHz operation dictated by the Operating System
97  *
98  * 5.9GHz Country List is a set of 6 bytes that represent a bitmask of countries
99  * in which 5.9GHz operation is enabled
100  *
101  * Validation:
102  *   - Verify that there are MTCL_SIZE bytes.
103  *   - Verify that the name is MTCL_NAME.
104  *   - Verify that the version is less than or equal to MAX_MTCL_VERSION.
105  *   - Verify that the support bytes are less than or equal to the
106  *      MAX_SUPPORT_STATE.
107  *
108  * Returns 0 for a valid file, -1 for an invalid file.
109  */
validate_mtcl(uint8_t * mtcl_bytes,size_t count)110 int validate_mtcl(uint8_t *mtcl_bytes, size_t count)
111 {
112 	if (!mtcl_bytes) {
113 		printk(BIOS_ERR, "Failed to get the %s file size!\n",
114 			  WIFI_MTCL_CBFS_DEFAULT_FILENAME);
115 		return -1;
116 	}
117 
118 	if (count != sizeof(struct wifi_mtcl)) {
119 		printk(BIOS_ERR, "Size of file read was: %zu, expected: %zu\n",
120 			  count, sizeof(struct wifi_mtcl));
121 		return -1;
122 	}
123 
124 	struct wifi_mtcl *mtcl = (struct wifi_mtcl *)mtcl_bytes;
125 
126 	if (strncmp(((char *)mtcl->name), MTCL_NAME, NAME_SIZE)) {
127 		printk(BIOS_ERR, "MTCL string not present but expected\n");
128 		return -1;
129 	}
130 
131 	if (mtcl->revision > MAX_VERSION) {
132 		printk(BIOS_ERR, "MTCL version too high\n");
133 		return -1;
134 	}
135 
136 	if (mtcl->support_6ghz > MAX_SUPPORT_STATE) {
137 		printk(BIOS_ERR, "MTCL 6GHz support state too high\n");
138 		return -1;
139 	}
140 
141 	if (mtcl->support_5p9ghz > MAX_SUPPORT_STATE) {
142 		printk(BIOS_ERR, "MTCL 5.9GHz support state too high\n");
143 		return -1;
144 	}
145 
146 	return 0;
147 }
148 
149 /*
150  * Retrieve WiFi MTCL data from CBFS, decode it, validate it and write it to
151  * AML.
152  *
153  * Returns the number of bytes read.
154  */
write_mtcl_function(void)155 void write_mtcl_function(void)
156 {
157 	size_t mtcl_bin_len;
158 	uint8_t *mtcl_bin;
159 
160 	mtcl_bin = cbfs_map(WIFI_MTCL_CBFS_DEFAULT_FILENAME, &mtcl_bin_len);
161 
162 	if (validate_mtcl(mtcl_bin, mtcl_bin_len) == 0)
163 		write_mtcl_aml(mtcl_bin, mtcl_bin_len);
164 
165 	cbfs_unmap(mtcl_bin);
166 }
167