[Nouveau] [PATCH 8/8] secboot: generate HS BL descriptor in hook
Alexandre Courbot
acourbot at nvidia.com
Tue Oct 11 06:45:53 UTC 2016
Use the HS hook to completely generate the HS BL descriptor, similarly
to what is done in the LS hook, instead of (arbitrarily) using the the
acr_v1 format as an intermediate.
This allows us to make the bootloader descriptor structures private to
each implementation, resulting in a cleaner an more consistent design.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drm/nouveau/nvkm/subdev/secboot/acr_v1.c | 145 +++++++++++++------------
drm/nouveau/nvkm/subdev/secboot/acr_v1.h | 142 ++++++++++++------------
drm/nouveau/nvkm/subdev/secboot/acr_v1_gm20b.c | 57 ++++------
3 files changed, 165 insertions(+), 179 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/secboot/acr_v1.c b/drm/nouveau/nvkm/subdev/secboot/acr_v1.c
index 68bb3113fd23..486327e5441a 100644
--- a/drm/nouveau/nvkm/subdev/secboot/acr_v1.c
+++ b/drm/nouveau/nvkm/subdev/secboot/acr_v1.c
@@ -82,6 +82,42 @@ struct ls_ucode_mgr {
struct list_head img_list;
};
+/**
+ * struct acr_v1_bl_desc - DMEM bootloader descriptor
+ * @signature: 16B signature for secure code. 0s if no secure code
+ * @ctx_dma: DMA context to be used by BL while loading code/data
+ * @code_dma_base: 256B-aligned Physical FB Address where code is located
+ * (falcon's $xcbase register)
+ * @non_sec_code_off: offset from code_dma_base where the non-secure code is
+ * located. The offset must be multiple of 256 to help perf
+ * @non_sec_code_size: the size of the nonSecure code part.
+ * @sec_code_off: offset from code_dma_base where the secure code is
+ * located. The offset must be multiple of 256 to help perf
+ * @sec_code_size: offset from code_dma_base where the secure code is
+ * located. The offset must be multiple of 256 to help perf
+ * @code_entry_point: code entry point which will be invoked by BL after
+ * code is loaded.
+ * @data_dma_base: 256B aligned Physical FB Address where data is located.
+ * (falcon's $xdbase register)
+ * @data_size: size of data block. Should be multiple of 256B
+ *
+ * Structure used by the bootloader to load the rest of the code. This has
+ * to be filled by host and copied into DMEM at offset provided in the
+ * hsflcn_bl_desc.bl_desc_dmem_load_off.
+ */
+struct acr_v1_bl_desc {
+ u32 reserved[4];
+ u32 signature[4];
+ u32 ctx_dma;
+ struct flcn_u64 code_dma_base;
+ u32 non_sec_code_off;
+ u32 non_sec_code_size;
+ u32 sec_code_off;
+ u32 sec_code_size;
+ u32 code_entry_point;
+ struct flcn_u64 data_dma_base;
+ u32 data_size;
+};
/*
*
@@ -115,21 +151,6 @@ struct hsf_fw_header {
};
/**
- * struct hsf_load_header - HS firmware load header
- */
-struct hsf_load_header {
- u32 non_sec_code_off;
- u32 non_sec_code_size;
- u32 data_dma_base;
- u32 data_size;
- u32 num_apps;
- struct {
- u32 sec_code_off;
- u32 sec_code_size;
- } app[0];
-};
-
-/**
* struct hsflcn_acr_desc - data section of the HS firmware
*
* This header is to be copied at the beginning of DMEM by the HS bootloader.
@@ -714,32 +735,21 @@ acr_v1_hsf_patch_signature(struct nvkm_acr_v1 *acr, void *acr_image)
* acr_v1_populate_hsf_bl_desc() - populate BL descriptor for HS image
*/
static void
-acr_v1_populate_hsf_bl_desc(void *acr_image, struct acr_v1_bl_desc *bl_desc)
+acr_v1_generate_hsf_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
+ u64 offset)
{
- struct fw_bin_header *hsbin_hdr = acr_image;
- struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset;
- struct hsf_load_header *load_hdr = acr_image + fw_hdr->hdr_offset;
+ struct acr_v1_bl_desc *bl_desc = _bl_desc;
- /*
- * Descriptor for the bootloader that will load the ACR image into
- * IMEM/DMEM memory.
- */
- fw_hdr = acr_image + hsbin_hdr->header_offset;
- load_hdr = acr_image + fw_hdr->hdr_offset;
memset(bl_desc, 0, sizeof(*bl_desc));
bl_desc->ctx_dma = FALCON_DMAIDX_VIRT;
- bl_desc->non_sec_code_off = load_hdr->non_sec_code_off;
- bl_desc->non_sec_code_size = load_hdr->non_sec_code_size;
- bl_desc->sec_code_off = load_hdr->app[0].sec_code_off;
- bl_desc->sec_code_size = load_hdr->app[0].sec_code_size;
+ bl_desc->non_sec_code_off = hdr->non_sec_code_off;
+ bl_desc->non_sec_code_size = hdr->non_sec_code_size;
+ bl_desc->sec_code_off = hdr->app[0].sec_code_off;
+ bl_desc->sec_code_size = hdr->app[0].sec_code_size;
bl_desc->code_entry_point = 0;
- /*
- * We need to set code_dma_base to the virtual address of the acr_blob,
- * and add this address to data_dma_base before writing it into DMEM
- */
- bl_desc->code_dma_base.lo = 0;
- bl_desc->data_dma_base.lo = load_hdr->data_dma_base;
- bl_desc->data_size = load_hdr->data_size;
+ bl_desc->code_dma_base = u64_to_flcn64(offset);
+ bl_desc->data_dma_base = u64_to_flcn64(offset + hdr->data_dma_base);
+ bl_desc->data_size = hdr->data_size;
}
static void
@@ -779,38 +789,44 @@ acr_v1_fixup_hs_desc(struct nvkm_acr_v1 *acr, struct nvkm_secboot *sb,
static int
acr_v1_prepare_hs_blob(struct nvkm_acr_v1 *acr, struct nvkm_secboot *sb,
const char *fw, struct nvkm_gpuobj **blob,
- struct acr_v1_bl_desc *bl_desc, bool patch)
+ struct hsf_load_header *load_header, bool patch)
{
const struct nvkm_subdev *subdev = acr->base.subdev;
void *acr_image;
struct fw_bin_header *hsbin_hdr;
+ struct hsf_fw_header *fw_hdr;
+ struct hsf_load_header *load_hdr;
void *acr_data;
int ret;
acr_image = acr_v1_load_firmware(subdev, fw, 0);
if (IS_ERR(acr_image))
return PTR_ERR(acr_image);
+
hsbin_hdr = acr_image;
+ fw_hdr = acr_image + hsbin_hdr->header_offset;
+ load_hdr = acr_image + fw_hdr->hdr_offset;
+ acr_data = acr_image + hsbin_hdr->data_offset;
/* Patch signature */
acr_v1_hsf_patch_signature(acr, acr_image);
- acr_data = acr_image + hsbin_hdr->data_offset;
-
/* Patch descriptor with WPR information? */
if (patch) {
- struct hsf_fw_header *fw_hdr;
- struct hsf_load_header *load_hdr;
struct hsflcn_acr_desc *desc;
- fw_hdr = acr_image + hsbin_hdr->header_offset;
- load_hdr = acr_image + fw_hdr->hdr_offset;
desc = acr_data + load_hdr->data_dma_base;
acr_v1_fixup_hs_desc(acr, sb, desc);
}
- /* Generate HS BL descriptor */
- acr_v1_populate_hsf_bl_desc(acr_image, bl_desc);
+ if (load_hdr->num_apps > ACR_V1_MAX_APPS) {
+ nvkm_error(subdev, "more apps (%d) than supported (%d)!",
+ load_hdr->num_apps, ACR_V1_MAX_APPS);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ memcpy(load_header, load_hdr, sizeof(*load_header) +
+ (sizeof(load_hdr->app[0]) * load_hdr->num_apps));
/* Create ACR blob and copy HS data to it */
ret = nvkm_gpuobj_new(subdev->device, ALIGN(hsbin_hdr->data_size, 256),
@@ -879,7 +895,7 @@ acr_v1_load_blobs(struct nvkm_acr_v1 *acr, struct nvkm_secboot *sb)
/* Load the HS firmware that will load the LS firmwares */
ret = acr_v1_prepare_hs_blob(acr, sb, "acr/ucode_load", &acr->load_blob,
- &acr->load_bl_desc, true);
+ &acr->load_bl_header, true);
if (ret)
return ret;
@@ -887,7 +903,7 @@ acr_v1_load_blobs(struct nvkm_acr_v1 *acr, struct nvkm_secboot *sb)
if (sb->wpr_size == 0) {
ret = acr_v1_prepare_hs_blob(acr, sb, "acr/ucode_unload",
&acr->unload_blob,
- &acr->unload_bl_desc, false);
+ &acr->unload_bl_header, false);
if (ret)
return ret;
}
@@ -918,30 +934,21 @@ acr_v1_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
void *hsbl_data = blob_data + hsbl_desc->data_off;
u32 code_size = ALIGN(hsbl_desc->code_size, 256);
const u32 base = sb->base;
- const u32 bl_desc_size = acr->func->bl_desc_size;
- struct acr_v1_bl_desc desc;
+ const u32 bl_desc_size = acr->hs_func->bl_desc_size;
+ const struct hsf_load_header *load_hdr;
u8 bl_desc[bl_desc_size];
- u64 vma_addr;
u32 code_start;
/* Find the bootloader descriptor for our blob and copy it */
if (blob == acr->load_blob) {
- desc = acr->load_bl_desc;
+ load_hdr = &acr->load_bl_header;
} else if (blob == acr->unload_blob) {
- desc = acr->unload_bl_desc;
+ load_hdr = &acr->unload_bl_header;
} else {
nvkm_error(_acr->subdev, "invalid secure boot blob!\n");
return -EINVAL;
}
- /* Patch the descriptor's load addresses */
- vma_addr = flcn64_to_u64(desc.code_dma_base) + offset;
- desc.code_dma_base.lo = lower_32_bits(vma_addr);
- desc.code_dma_base.hi = upper_32_bits(vma_addr);
- vma_addr = flcn64_to_u64(desc.data_dma_base) + offset;
- desc.data_dma_base.lo = lower_32_bits(vma_addr);
- desc.data_dma_base.hi = upper_32_bits(vma_addr);
-
/*
* Copy HS bootloader data
*/
@@ -954,8 +961,8 @@ acr_v1_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
nvkm_falcon_load_imem(device, sb->base, hsbl_code, code_start,
code_size, hsbl_desc->start_tag);
- /* Fixup the BL header */
- acr->func->generate_bl_desc(&desc, &bl_desc);
+ /* Generate the BL header */
+ acr->hs_func->generate_bl_desc(load_hdr, bl_desc, offset);
/*
* Copy HS BL header where the HS descriptor expects it to be
@@ -966,12 +973,6 @@ acr_v1_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
return 0;
}
-static void
-acr_v1_generate_bl_desc(const struct acr_v1_bl_desc *desc, void *ret)
-{
- memcpy(ret, desc, sizeof(*desc));
-}
-
/*
* acr_v1_reset() - execute secure boot from the prepared state
*
@@ -1079,10 +1080,10 @@ acr_v1_dtor(struct nvkm_acr *_acr)
kfree(acr);
}
-static const struct nvkm_acr_v1_func
-gm200_acr_v1_func = {
+static const struct nvkm_acr_v1_hs_func
+gm200_acr_v1_hs_func = {
+ .generate_bl_desc = acr_v1_generate_hsf_bl_desc,
.bl_desc_size = sizeof(struct acr_v1_bl_desc),
- .generate_bl_desc = acr_v1_generate_bl_desc,
};
static const nvkm_acr_v1_ls_func
@@ -1121,7 +1122,7 @@ nvkm_acr_v1_new(void)
return ERR_PTR(-ENOMEM);
acr->base.func = &acr_v1_func;
- acr->func = &gm200_acr_v1_func;
+ acr->hs_func = &gm200_acr_v1_hs_func;
acr->ls_func = &acr_v1_ls_func;
return &acr->base;
diff --git a/drm/nouveau/nvkm/subdev/secboot/acr_v1.h b/drm/nouveau/nvkm/subdev/secboot/acr_v1.h
index a3111c1ed9fb..a84867413df8 100644
--- a/drm/nouveau/nvkm/subdev/secboot/acr_v1.h
+++ b/drm/nouveau/nvkm/subdev/secboot/acr_v1.h
@@ -48,43 +48,6 @@ static inline struct flcn_u64 u64_to_flcn64(const u64 u)
return ret;
}
-/**
- * struct acr_v1_bl_desc - DMEM bootloader descriptor
- * @signature: 16B signature for secure code. 0s if no secure code
- * @ctx_dma: DMA context to be used by BL while loading code/data
- * @code_dma_base: 256B-aligned Physical FB Address where code is located
- * (falcon's $xcbase register)
- * @non_sec_code_off: offset from code_dma_base where the non-secure code is
- * located. The offset must be multiple of 256 to help perf
- * @non_sec_code_size: the size of the nonSecure code part.
- * @sec_code_off: offset from code_dma_base where the secure code is
- * located. The offset must be multiple of 256 to help perf
- * @sec_code_size: offset from code_dma_base where the secure code is
- * located. The offset must be multiple of 256 to help perf
- * @code_entry_point: code entry point which will be invoked by BL after
- * code is loaded.
- * @data_dma_base: 256B aligned Physical FB Address where data is located.
- * (falcon's $xdbase register)
- * @data_size: size of data block. Should be multiple of 256B
- *
- * Structure used by the bootloader to load the rest of the code. This has
- * to be filled by host and copied into DMEM at offset provided in the
- * hsflcn_bl_desc.bl_desc_dmem_load_off.
- */
-struct acr_v1_bl_desc {
- u32 reserved[4];
- u32 signature[4];
- u32 ctx_dma;
- struct flcn_u64 code_dma_base;
- u32 non_sec_code_off;
- u32 non_sec_code_size;
- u32 sec_code_off;
- u32 sec_code_size;
- u32 code_entry_point;
- struct flcn_u64 data_dma_base;
- u32 data_size;
-};
-
/*
*
* LS blob structures
@@ -110,6 +73,33 @@ struct lsf_ucode_desc {
};
/**
+ * struct lsf_wpr_header - LS blob WPR Header
+ * @falcon_id: LS falcon ID
+ * @lsb_offset: offset of the lsb_lsf_header in the WPR region
+ * @bootstrap_owner: secure falcon reponsible for bootstrapping the LS falcon
+ * @lazy_bootstrap: skip bootstrapping by ACR
+ * @status: bootstrapping status
+ *
+ * An array of these is written at the beginning of the WPR region, one for
+ * each managed falcon. The array is terminated by an instance which falcon_id
+ * is LSF_FALCON_ID_INVALID.
+ */
+struct lsf_wpr_header {
+ u32 falcon_id;
+ u32 lsb_offset;
+ u32 bootstrap_owner;
+ u32 lazy_bootstrap;
+ u32 status;
+#define LSF_IMAGE_STATUS_NONE 0
+#define LSF_IMAGE_STATUS_COPY 1
+#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED 2
+#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED 3
+#define LSF_IMAGE_STATUS_VALIDATION_DONE 4
+#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED 5
+#define LSF_IMAGE_STATUS_BOOTSTRAP_READY 6
+};
+
+/**
* struct lsf_lsb_header - LS firmware header
* @signature: signature to verify the firmware against
* @ucode_off: offset of the ucode blob in the WPR region. The ucode
@@ -151,34 +141,6 @@ struct lsf_lsb_header {
};
/**
- * struct lsf_wpr_header - LS blob WPR Header
- * @falcon_id: LS falcon ID
- * @lsb_offset: offset of the lsb_lsf_header in the WPR region
- * @bootstrap_owner: secure falcon reponsible for bootstrapping the LS falcon
- * @lazy_bootstrap: skip bootstrapping by ACR
- * @status: bootstrapping status
- *
- * An array of these is written at the beginning of the WPR region, one for
- * each managed falcon. The array is terminated by an instance which falcon_id
- * is LSF_FALCON_ID_INVALID.
- */
-struct lsf_wpr_header {
- u32 falcon_id;
- u32 lsb_offset;
- u32 bootstrap_owner;
- u32 lazy_bootstrap;
- u32 status;
-#define LSF_IMAGE_STATUS_NONE 0
-#define LSF_IMAGE_STATUS_COPY 1
-#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED 2
-#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED 3
-#define LSF_IMAGE_STATUS_VALIDATION_DONE 4
-#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED 5
-#define LSF_IMAGE_STATUS_BOOTSTRAP_READY 6
-};
-
-
-/**
* struct ls_ucode_img_desc - descriptor of firmware image
* @descriptor_size: size of this descriptor
* @image_size: size of the whole image
@@ -254,16 +216,43 @@ struct ls_ucode_img {
struct lsf_lsb_header lsb_header;
};
+
+
+/*
+ *
+ * HS blob structures
+ *
+ */
+
+
+struct hsf_load_header_app {
+ u32 sec_code_off;
+ u32 sec_code_size;
+};
+
+/**
+ * struct hsf_load_header - HS firmware load header
+ */
+struct hsf_load_header {
+ u32 non_sec_code_off;
+ u32 non_sec_code_size;
+ u32 data_dma_base;
+ u32 data_size;
+ u32 num_apps;
+ struct hsf_load_header_app app[0];
+};
+
+
/**
- * struct nvkm_acr_v1_func - manages nuances between ACR v1 instances
+ * struct nvkm_acr_v1_hs_func - manages nuances between ACR v1 HS instances
*
- * @bl_desc_size: size of the bootloader descriptor
* @generate_bl_desc: function called on a block of bl_desc_size to generate the
* proper bootloader descriptor
+ * @bl_desc_size: size of the ootloader descriptor
*/
-struct nvkm_acr_v1_func {
+struct nvkm_acr_v1_hs_func {
+ void (*generate_bl_desc)(const struct hsf_load_header *, void *, u64);
u32 bl_desc_size;
- void (*generate_bl_desc)(const struct acr_v1_bl_desc *, void *);
};
/**
@@ -289,6 +278,9 @@ nvkm_acr_v1_ls_func[NVKM_FALCON_END];
int acr_v1_ls_load_fecs(const struct nvkm_subdev *, struct ls_ucode_img *);
int acr_v1_ls_load_gpccs(const struct nvkm_subdev *, struct ls_ucode_img *);
+
+#define ACR_V1_MAX_APPS 8
+
/**
* struct nvkm_acr_v1 - version 1 of the ACR
*
@@ -303,7 +295,7 @@ int acr_v1_ls_load_gpccs(const struct nvkm_subdev *, struct ls_ucode_img *);
*/
struct nvkm_acr_v1 {
struct nvkm_acr base;
- const struct nvkm_acr_v1_func *func;
+ const struct nvkm_acr_v1_hs_func *hs_func;
const nvkm_acr_v1_ls_func *ls_func;
/*
@@ -311,11 +303,17 @@ struct nvkm_acr_v1 {
* on Tegra the HS FW copies the LS blob into the fixed WPR instead
*/
struct nvkm_gpuobj *load_blob;
- struct acr_v1_bl_desc load_bl_desc;
+ struct {
+ struct hsf_load_header load_bl_header;
+ struct hsf_load_header_app __load_apps[ACR_V1_MAX_APPS];
+ };
/* HS FW - unlock WPR region (dGPU only) */
struct nvkm_gpuobj *unload_blob;
- struct acr_v1_bl_desc unload_bl_desc;
+ struct {
+ struct hsf_load_header unload_bl_header;
+ struct hsf_load_header_app __unload_apps[ACR_V1_MAX_APPS];
+ };
/* HS bootloader */
void *hsbl_blob;
diff --git a/drm/nouveau/nvkm/subdev/secboot/acr_v1_gm20b.c b/drm/nouveau/nvkm/subdev/secboot/acr_v1_gm20b.c
index 1062d4f6a1ff..e59dc8e5437b 100644
--- a/drm/nouveau/nvkm/subdev/secboot/acr_v1_gm20b.c
+++ b/drm/nouveau/nvkm/subdev/secboot/acr_v1_gm20b.c
@@ -42,37 +42,6 @@ struct acr_v1_gm20b_bl_desc {
u32 data_size;
};
-/**
- * gm20b_acr_v1_generate_bl_desc - write BL descriptor in format used by GM20B
- *
- * There is only a slight format difference (DMA addresses being 32-bits and
- * 256B-aligned) to address.
- */
-static void
-gm20b_acr_v1_generate_bl_desc(const struct acr_v1_bl_desc *desc, void *ret)
-{
- struct acr_v1_gm20b_bl_desc *gdesc = ret;
- u64 addr;
-
- memcpy(gdesc->reserved, desc->reserved, sizeof(gdesc->reserved));
- memcpy(gdesc->signature, desc->signature, sizeof(gdesc->signature));
- gdesc->ctx_dma = desc->ctx_dma;
- addr = desc->code_dma_base.hi;
- addr <<= 32;
- addr |= desc->code_dma_base.lo;
- gdesc->code_dma_base = lower_32_bits(addr >> 8);
- gdesc->non_sec_code_off = desc->non_sec_code_off;
- gdesc->non_sec_code_size = desc->non_sec_code_size;
- gdesc->sec_code_off = desc->sec_code_off;
- gdesc->sec_code_size = desc->sec_code_size;
- gdesc->code_entry_point = desc->code_entry_point;
- addr = desc->data_dma_base.hi;
- addr <<= 32;
- addr |= desc->data_dma_base.lo;
- gdesc->data_dma_base = lower_32_bits(addr >> 8);
- gdesc->data_size = desc->data_size;
-}
-
static void
acr_v1_gm20b_ls_generic_bl_desc(const struct ls_ucode_img *img, u64 wpr_addr,
void *_desc)
@@ -92,10 +61,28 @@ acr_v1_gm20b_ls_generic_bl_desc(const struct ls_ucode_img *img, u64 wpr_addr,
desc->code_entry_point = pdesc->app_imem_entry;
}
-static const struct nvkm_acr_v1_func
-gm20b_acr_v1_func = {
+static void
+gm20b_acr_v1_generate_hsf_bl_desc(const struct hsf_load_header *load_hdr,
+ void *_bl_desc, u64 offset)
+{
+ struct acr_v1_gm20b_bl_desc *bl_desc = _bl_desc;
+
+ memset(bl_desc, 0, sizeof(*bl_desc));
+ bl_desc->ctx_dma = FALCON_DMAIDX_VIRT;
+ bl_desc->non_sec_code_off = load_hdr->non_sec_code_off;
+ bl_desc->non_sec_code_size = load_hdr->non_sec_code_size;
+ bl_desc->sec_code_off = load_hdr->app[0].sec_code_off;
+ bl_desc->sec_code_size = load_hdr->app[0].sec_code_size;
+ bl_desc->code_entry_point = 0;
+ bl_desc->code_dma_base = offset >> 8;
+ bl_desc->data_dma_base = (offset + load_hdr->data_dma_base) >> 8;
+ bl_desc->data_size = load_hdr->data_size;
+}
+
+static const struct nvkm_acr_v1_hs_func
+gm20b_acr_v1_hs_func = {
+ .generate_bl_desc = gm20b_acr_v1_generate_hsf_bl_desc,
.bl_desc_size = sizeof(struct acr_v1_gm20b_bl_desc),
- .generate_bl_desc = gm20b_acr_v1_generate_bl_desc,
};
static const struct nvkm_acr_func
@@ -128,7 +115,7 @@ nvkm_acr_v1_gm20b_new(void)
return ERR_PTR(-ENOMEM);
acr->base.func = &gm20b_acr_func;
- acr->func = &gm20b_acr_v1_func;
+ acr->hs_func = &gm20b_acr_v1_hs_func;
acr->ls_func = &gm20b_acr_v1_ls_func;
return &acr->base;
--
2.10.0
More information about the Nouveau
mailing list